mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Desktop: Switch to the latest unreleased version of Winit (#3177)
* Use unstable winit * Improve * Remove unnecessary heap indirection
This commit is contained in:
@@ -26,7 +26,7 @@ graphite-desktop-wrapper = { path = "wrapper" }
|
||||
graphite-desktop-embedded-resources = { path = "embedded-resources", optional = true }
|
||||
|
||||
wgpu = { workspace = true }
|
||||
winit = { workspace = true, features = ["serde"] }
|
||||
winit = { workspace = true, features = [ "serde" ] }
|
||||
thiserror = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
cef = { workspace = true }
|
||||
@@ -34,7 +34,7 @@ cef-dll-sys = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
ron = { workspace = true}
|
||||
ron = { workspace = true }
|
||||
bytemuck = { workspace = true }
|
||||
glam = { workspace = true }
|
||||
vello = { workspace = true }
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
use crate::CustomEvent;
|
||||
use crate::cef::WindowSize;
|
||||
use crate::consts::{APP_NAME, CEF_MESSAGE_LOOP_MAX_ITERATIONS};
|
||||
use crate::persist::PersistentData;
|
||||
use crate::render::GraphicsState;
|
||||
use graphite_desktop_wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, Platform};
|
||||
use graphite_desktop_wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
|
||||
|
||||
use rfd::AsyncFileDialog;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::mpsc::SyncSender;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::event_loop::ControlFlow;
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowId;
|
||||
|
||||
use crate::cef;
|
||||
use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS;
|
||||
use crate::event::{AppEvent, AppEventScheduler};
|
||||
use crate::native_window;
|
||||
use crate::persist::PersistentData;
|
||||
use crate::render::GraphicsState;
|
||||
use graphite_desktop_wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, Platform};
|
||||
use graphite_desktop_wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
|
||||
|
||||
pub(crate) struct WinitApp {
|
||||
pub(crate) struct App {
|
||||
cef_context: Box<dyn cef::CefContext>,
|
||||
window: Option<Arc<Window>>,
|
||||
window: Option<Arc<dyn Window>>,
|
||||
native_window: native_window::NativeWindowHandle,
|
||||
cef_schedule: Option<Instant>,
|
||||
window_size_sender: Sender<WindowSize>,
|
||||
cef_window_size_sender: Sender<cef::WindowSize>,
|
||||
graphics_state: Option<GraphicsState>,
|
||||
wgpu_context: WgpuContext,
|
||||
event_loop_proxy: EventLoopProxy<CustomEvent>,
|
||||
app_event_receiver: Receiver<AppEvent>,
|
||||
app_event_scheduler: AppEventScheduler,
|
||||
desktop_wrapper: DesktopWrapper,
|
||||
last_ui_update: Instant,
|
||||
avg_frame_time: f32,
|
||||
@@ -43,14 +41,20 @@ pub(crate) struct WinitApp {
|
||||
persistent_data: PersistentData,
|
||||
}
|
||||
|
||||
impl WinitApp {
|
||||
pub(crate) fn new(cef_context: Box<dyn cef::CefContext>, window_size_sender: Sender<WindowSize>, wgpu_context: WgpuContext, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
|
||||
let rendering_loop_proxy = event_loop_proxy.clone();
|
||||
impl App {
|
||||
pub(crate) fn new(
|
||||
cef_context: Box<dyn cef::CefContext>,
|
||||
window_size_sender: Sender<cef::WindowSize>,
|
||||
wgpu_context: WgpuContext,
|
||||
app_event_receiver: Receiver<AppEvent>,
|
||||
app_event_scheduler: AppEventScheduler,
|
||||
) -> Self {
|
||||
let rendering_app_event_scheduler = app_event_scheduler.clone();
|
||||
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
let result = futures::executor::block_on(DesktopWrapper::execute_node_graph());
|
||||
let _ = rendering_loop_proxy.send_event(CustomEvent::NodeGraphExecutionResult(result));
|
||||
rendering_app_event_scheduler.schedule(AppEvent::NodeGraphExecutionResult(result));
|
||||
let _ = start_render_receiver.recv();
|
||||
}
|
||||
});
|
||||
@@ -63,9 +67,10 @@ impl WinitApp {
|
||||
window: None,
|
||||
cef_schedule: Some(Instant::now()),
|
||||
graphics_state: None,
|
||||
window_size_sender,
|
||||
cef_window_size_sender: window_size_sender,
|
||||
wgpu_context,
|
||||
event_loop_proxy,
|
||||
app_event_receiver,
|
||||
app_event_scheduler,
|
||||
desktop_wrapper: DesktopWrapper::new(),
|
||||
last_ui_update: Instant::now(),
|
||||
avg_frame_time: 0.,
|
||||
@@ -87,7 +92,7 @@ impl WinitApp {
|
||||
self.send_or_queue_web_message(bytes);
|
||||
}
|
||||
DesktopFrontendMessage::OpenFileDialog { title, filters, context } => {
|
||||
let event_loop_proxy = self.event_loop_proxy.clone();
|
||||
let app_event_scheduler = self.app_event_scheduler.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let mut dialog = AsyncFileDialog::new().set_title(title);
|
||||
for filter in filters {
|
||||
@@ -100,7 +105,7 @@ impl WinitApp {
|
||||
&& let Ok(content) = std::fs::read(&path)
|
||||
{
|
||||
let message = DesktopWrapperMessage::OpenFileDialogResult { path, content, context };
|
||||
let _ = event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(message));
|
||||
app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -111,7 +116,7 @@ impl WinitApp {
|
||||
filters,
|
||||
context,
|
||||
} => {
|
||||
let event_loop_proxy = self.event_loop_proxy.clone();
|
||||
let app_event_scheduler = self.app_event_scheduler.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let mut dialog = AsyncFileDialog::new().set_title(title).set_file_name(default_filename);
|
||||
if let Some(folder) = default_folder {
|
||||
@@ -125,7 +130,7 @@ impl WinitApp {
|
||||
|
||||
if let Some(path) = futures::executor::block_on(show_dialog) {
|
||||
let message = DesktopWrapperMessage::SaveFileDialogResult { path, context };
|
||||
let _ = event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(message));
|
||||
app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -145,7 +150,7 @@ impl WinitApp {
|
||||
if let Some(graphics_state) = &mut self.graphics_state
|
||||
&& let Some(window) = &self.window
|
||||
{
|
||||
let window_size = window.inner_size();
|
||||
let window_size = window.surface_size();
|
||||
|
||||
let viewport_offset_x = x / window_size.width as f32;
|
||||
let viewport_offset_y = y / window_size.height as f32;
|
||||
@@ -173,7 +178,7 @@ impl WinitApp {
|
||||
}
|
||||
}
|
||||
DesktopFrontendMessage::CloseWindow => {
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::CloseWindow);
|
||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||
}
|
||||
DesktopFrontendMessage::PersistenceWriteDocument { id, document } => {
|
||||
self.persistent_data.write_document(id, document);
|
||||
@@ -252,43 +257,67 @@ impl WinitApp {
|
||||
self.web_communication_startup_buffer.push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
// Set a timeout in case we miss any cef schedule requests
|
||||
let timeout = Instant::now() + Duration::from_millis(10);
|
||||
let wait_until = timeout.min(self.cef_schedule.unwrap_or(timeout));
|
||||
if let Some(schedule) = self.cef_schedule
|
||||
&& schedule < Instant::now()
|
||||
{
|
||||
self.cef_schedule = None;
|
||||
// Poll cef message loop multiple times to avoid message loop starvation
|
||||
for _ in 0..CEF_MESSAGE_LOOP_MAX_ITERATIONS {
|
||||
self.cef_context.work();
|
||||
fn user_event(&mut self, event_loop: &dyn ActiveEventLoop, event: AppEvent) {
|
||||
match event {
|
||||
AppEvent::WebCommunicationInitialized => {
|
||||
self.web_communication_initialized = true;
|
||||
for message in self.web_communication_startup_buffer.drain(..) {
|
||||
self.cef_context.send_web_message(message);
|
||||
}
|
||||
}
|
||||
AppEvent::DesktopWrapperMessage(message) => self.dispatch_desktop_wrapper_message(message),
|
||||
AppEvent::NodeGraphExecutionResult(result) => match result {
|
||||
NodeGraphExecutionResult::HasRun(texture) => {
|
||||
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::PollNodeGraphEvaluation);
|
||||
if let Some(texture) = texture
|
||||
&& let Some(graphics_state) = self.graphics_state.as_mut()
|
||||
&& let Some(window) = self.window.as_ref()
|
||||
{
|
||||
graphics_state.bind_viewport_texture(texture);
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
NodeGraphExecutionResult::NotRun => {}
|
||||
},
|
||||
AppEvent::UiUpdate(texture) => {
|
||||
if let Some(graphics_state) = self.graphics_state.as_mut() {
|
||||
graphics_state.resize(texture.width(), texture.height());
|
||||
graphics_state.bind_ui_texture(texture);
|
||||
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
|
||||
self.last_ui_update = Instant::now();
|
||||
if elapsed < 0.5 {
|
||||
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
|
||||
}
|
||||
}
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
AppEvent::ScheduleBrowserWork(instant) => {
|
||||
if instant <= Instant::now() {
|
||||
self.cef_context.work();
|
||||
} else {
|
||||
self.cef_schedule = Some(instant);
|
||||
}
|
||||
}
|
||||
AppEvent::CloseWindow => {
|
||||
// TODO: Implement graceful shutdown
|
||||
|
||||
tracing::info!("Exiting main event loop");
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
if let Some(window) = &self.window.as_ref() {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
|
||||
}
|
||||
}
|
||||
impl ApplicationHandler for App {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = self.native_window.build(event_loop);
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let mut window = Window::default_attributes()
|
||||
.with_title(APP_NAME)
|
||||
.with_min_inner_size(winit::dpi::LogicalSize::new(400, 300))
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(1200, 800))
|
||||
.with_resizable(true);
|
||||
let window: Arc<dyn Window> = Arc::from(event_loop.create_window(window_attributes).unwrap());
|
||||
|
||||
window = self.native_window.build(window, event_loop);
|
||||
self.native_window.setup(window.as_ref());
|
||||
|
||||
let window = event_loop.create_window(window).unwrap();
|
||||
|
||||
self.native_window.setup(&window);
|
||||
|
||||
let window = Arc::new(window);
|
||||
let graphics_state = GraphicsState::new(window.clone(), self.wgpu_context.clone());
|
||||
|
||||
self.window = Some(window);
|
||||
@@ -307,67 +336,21 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::UpdatePlatform(platform));
|
||||
}
|
||||
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: CustomEvent) {
|
||||
match event {
|
||||
CustomEvent::WebCommunicationInitialized => {
|
||||
self.web_communication_initialized = true;
|
||||
for message in self.web_communication_startup_buffer.drain(..) {
|
||||
self.cef_context.send_web_message(message);
|
||||
}
|
||||
}
|
||||
CustomEvent::DesktopWrapperMessage(message) => self.dispatch_desktop_wrapper_message(message),
|
||||
CustomEvent::NodeGraphExecutionResult(result) => match result {
|
||||
NodeGraphExecutionResult::HasRun(texture) => {
|
||||
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::PollNodeGraphEvaluation);
|
||||
if let Some(texture) = texture
|
||||
&& let Some(graphics_state) = self.graphics_state.as_mut()
|
||||
&& let Some(window) = self.window.as_ref()
|
||||
{
|
||||
graphics_state.bind_viewport_texture(texture);
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
NodeGraphExecutionResult::NotRun => {}
|
||||
},
|
||||
CustomEvent::UiUpdate(texture) => {
|
||||
if let Some(graphics_state) = self.graphics_state.as_mut() {
|
||||
graphics_state.resize(texture.width(), texture.height());
|
||||
graphics_state.bind_ui_texture(texture);
|
||||
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
|
||||
self.last_ui_update = Instant::now();
|
||||
if elapsed < 0.5 {
|
||||
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
|
||||
}
|
||||
}
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
CustomEvent::ScheduleBrowserWork(instant) => {
|
||||
if instant <= Instant::now() {
|
||||
self.cef_context.work();
|
||||
} else {
|
||||
self.cef_schedule = Some(instant);
|
||||
}
|
||||
}
|
||||
CustomEvent::CloseWindow => {
|
||||
// TODO: Implement graceful shutdown
|
||||
|
||||
tracing::info!("Exiting main event loop");
|
||||
event_loop.exit();
|
||||
}
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
while let Ok(event) = self.app_event_receiver.try_recv() {
|
||||
self.user_event(event_loop, event);
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||
self.cef_context.handle_window_event(&event);
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::CloseWindow);
|
||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||
}
|
||||
WindowEvent::Resized(PhysicalSize { width, height }) => {
|
||||
let _ = self.window_size_sender.send(WindowSize::new(width as usize, height as usize));
|
||||
WindowEvent::SurfaceResized(size) => {
|
||||
let _ = self.cef_window_size_sender.send(size.into());
|
||||
self.cef_context.notify_of_resize();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
@@ -387,18 +370,19 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||
let _ = self.start_render_sender.try_send(());
|
||||
}
|
||||
}
|
||||
// Currently not supported on wayland see https://github.com/rust-windowing/winit/issues/1881
|
||||
WindowEvent::DroppedFile(path) => {
|
||||
match std::fs::read(&path) {
|
||||
Ok(content) => {
|
||||
let message = DesktopWrapperMessage::OpenFile { path, content };
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(message));
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to read dropped file {}: {}", path.display(), e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
WindowEvent::DragDropped { paths, .. } => {
|
||||
for path in paths {
|
||||
match std::fs::read(&path) {
|
||||
Ok(content) => {
|
||||
let message = DesktopWrapperMessage::OpenFile { path, content };
|
||||
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to read dropped file {}: {}", path.display(), e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -406,4 +390,24 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
|
||||
// Notify cef of possible input events
|
||||
self.cef_context.work();
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
// Set a timeout in case we miss any cef schedule requests
|
||||
let timeout = Instant::now() + Duration::from_millis(10);
|
||||
let wait_until = timeout.min(self.cef_schedule.unwrap_or(timeout));
|
||||
if let Some(schedule) = self.cef_schedule
|
||||
&& schedule < Instant::now()
|
||||
{
|
||||
self.cef_schedule = None;
|
||||
// Poll cef message loop multiple times to avoid message loop starvation
|
||||
for _ in 0..CEF_MESSAGE_LOOP_MAX_ITERATIONS {
|
||||
self.cef_context.work();
|
||||
}
|
||||
}
|
||||
if let Some(window) = &self.window.as_ref() {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
//! - **Windows**: D3D11 shared textures via either Vulkan or D3D12 interop (`accelerated_paint_d3d11` feature)
|
||||
//! - **macOS**: IOSurface via Metal/Vulkan interop (`accelerated_paint_iosurface` feature)
|
||||
//!
|
||||
//!
|
||||
//! The system gracefully falls back to CPU textures when hardware acceleration is unavailable.
|
||||
|
||||
use crate::CustomEvent;
|
||||
use crate::event::{AppEvent, AppEventScheduler};
|
||||
use crate::render::FrameBufferRef;
|
||||
use graphite_desktop_wrapper::{WgpuContext, deserialize_editor_message};
|
||||
use std::fs::File;
|
||||
@@ -38,7 +37,6 @@ mod texture_import;
|
||||
use texture_import::SharedTextureHandle;
|
||||
|
||||
pub(crate) use context::{CefContext, CefContextBuilder, InitError};
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
|
||||
pub(crate) trait CefEventHandler: Clone + Send + Sync + 'static {
|
||||
fn window_size(&self) -> WindowSize;
|
||||
@@ -58,12 +56,16 @@ pub(crate) struct WindowSize {
|
||||
pub(crate) width: usize,
|
||||
pub(crate) height: usize,
|
||||
}
|
||||
|
||||
impl WindowSize {
|
||||
pub(crate) fn new(width: usize, height: usize) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
}
|
||||
impl From<winit::dpi::PhysicalSize<u32>> for WindowSize {
|
||||
fn from(size: winit::dpi::PhysicalSize<u32>) -> Self {
|
||||
Self::new(size.width as usize, size.height as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Resource {
|
||||
@@ -88,29 +90,17 @@ impl Read for ResourceReader {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CefHandler {
|
||||
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
|
||||
event_loop_proxy: EventLoopProxy<CustomEvent>,
|
||||
wgpu_context: WgpuContext,
|
||||
app_event_scheduler: AppEventScheduler,
|
||||
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
|
||||
}
|
||||
|
||||
struct WindowSizeReceiver {
|
||||
receiver: Receiver<WindowSize>,
|
||||
window_size: WindowSize,
|
||||
}
|
||||
impl WindowSizeReceiver {
|
||||
fn new(window_size_receiver: Receiver<WindowSize>) -> Self {
|
||||
Self {
|
||||
window_size: WindowSize { width: 1, height: 1 },
|
||||
receiver: window_size_receiver,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl CefHandler {
|
||||
pub(crate) fn new(window_size_receiver: Receiver<WindowSize>, event_loop_proxy: EventLoopProxy<CustomEvent>, wgpu_context: WgpuContext) -> Self {
|
||||
pub(crate) fn new(wgpu_context: WgpuContext, app_event_scheduler: AppEventScheduler, window_size_receiver: Receiver<WindowSize>) -> Self {
|
||||
Self {
|
||||
window_size_receiver: Arc::new(Mutex::new(WindowSizeReceiver::new(window_size_receiver))),
|
||||
event_loop_proxy,
|
||||
wgpu_context,
|
||||
app_event_scheduler,
|
||||
window_size_receiver: Arc::new(Mutex::new(WindowSizeReceiver::new(window_size_receiver))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,14 +154,14 @@ impl CefEventHandler for CefHandler {
|
||||
},
|
||||
);
|
||||
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
|
||||
self.app_event_scheduler.schedule(AppEvent::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));
|
||||
self.app_event_scheduler.schedule(AppEvent::UiUpdate(texture));
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to import shared texture: {}", e);
|
||||
@@ -235,11 +225,11 @@ 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));
|
||||
self.app_event_scheduler.schedule(AppEvent::ScheduleBrowserWork(scheduled_time));
|
||||
}
|
||||
|
||||
fn initialized_web_communication(&self) {
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::WebCommunicationInitialized);
|
||||
self.app_event_scheduler.schedule(AppEvent::WebCommunicationInitialized);
|
||||
}
|
||||
|
||||
fn receive_web_message(&self, message: &[u8]) {
|
||||
@@ -247,6 +237,19 @@ impl CefEventHandler for CefHandler {
|
||||
tracing::error!("Failed to deserialize web message");
|
||||
return;
|
||||
};
|
||||
let _ = self.event_loop_proxy.send_event(CustomEvent::DesktopWrapperMessage(desktop_wrapper_message));
|
||||
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(desktop_wrapper_message));
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowSizeReceiver {
|
||||
window_size: WindowSize,
|
||||
receiver: Receiver<WindowSize>,
|
||||
}
|
||||
impl WindowSizeReceiver {
|
||||
fn new(window_size_receiver: Receiver<WindowSize>) -> Self {
|
||||
Self {
|
||||
window_size: WindowSize { width: 1, height: 1 },
|
||||
receiver: window_size_receiver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use cef::sys::{cef_event_flags_t, cef_key_event_type_t, cef_mouse_button_type_t}
|
||||
use cef::{Browser, ImplBrowser, ImplBrowserHost, KeyEvent, KeyEventType, MouseEvent};
|
||||
use std::time::Instant;
|
||||
use winit::dpi::PhysicalPosition;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
use winit::event::{ButtonSource, ElementState, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
|
||||
mod keymap;
|
||||
use keymap::{ToNativeKeycode, ToVKBits};
|
||||
@@ -11,7 +11,7 @@ use super::consts::{MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT, SCROLL_LINE_H
|
||||
|
||||
pub(crate) fn handle_window_event(browser: &Browser, input_state: &mut InputState, event: &WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
WindowEvent::PointerMoved { position, .. } => {
|
||||
input_state.cursor_move(position);
|
||||
|
||||
let Some(host) = browser.host() else {
|
||||
@@ -19,13 +19,20 @@ pub(crate) fn handle_window_event(browser: &Browser, input_state: &mut InputStat
|
||||
};
|
||||
host.send_mouse_move_event(Some(&input_state.into()), 0);
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let cef_click_count = input_state.mouse_input(button, state).into();
|
||||
WindowEvent::PointerButton { state, button, .. } => {
|
||||
let mouse_button = match button {
|
||||
ButtonSource::Mouse(mouse_button) => mouse_button,
|
||||
_ => {
|
||||
return; // TODO: Handle touch input
|
||||
}
|
||||
};
|
||||
|
||||
let cef_click_count = input_state.mouse_input(mouse_button, state).into();
|
||||
let cef_mouse_up = match state {
|
||||
ElementState::Pressed => 0,
|
||||
ElementState::Released => 1,
|
||||
};
|
||||
let cef_button = match button {
|
||||
let cef_button = match mouse_button {
|
||||
MouseButton::Left => cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_LEFT),
|
||||
MouseButton::Right => cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_RIGHT),
|
||||
MouseButton::Middle => cef::MouseButtonType::from(cef_mouse_button_type_t::MBT_MIDDLE),
|
||||
@@ -59,7 +66,6 @@ pub(crate) fn handle_window_event(browser: &Browser, input_state: &mut InputStat
|
||||
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,
|
||||
},
|
||||
@@ -312,7 +318,7 @@ impl CefModifiers {
|
||||
if input_state.modifiers.alt_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_ALT_DOWN as u32;
|
||||
}
|
||||
if input_state.modifiers.super_key() {
|
||||
if input_state.modifiers.meta_key() {
|
||||
inner |= cef_event_flags_t::EVENTFLAG_COMMAND_DOWN as u32;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,8 @@ impl ToVKBits for winit::keyboard::NamedKey {
|
||||
(0x91, ScrollLock),
|
||||
(0x10, Shift),
|
||||
(0x5B, Meta),
|
||||
(0x5C, Super),
|
||||
(0x0D, Enter),
|
||||
(0x09, Tab),
|
||||
(0x20, Space),
|
||||
(0x28, ArrowDown),
|
||||
(0x25, ArrowLeft),
|
||||
(0x27, ArrowRight),
|
||||
@@ -253,6 +251,7 @@ impl ToVKBits for char {
|
||||
(0xDE, '"'),
|
||||
(0xBF, '/'),
|
||||
(0xBF, '?'),
|
||||
(0x20, ' '),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
34
desktop/src/event.rs
Normal file
34
desktop/src/event.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use graphite_desktop_wrapper::NodeGraphExecutionResult;
|
||||
use graphite_desktop_wrapper::messages::DesktopWrapperMessage;
|
||||
|
||||
pub(crate) enum AppEvent {
|
||||
UiUpdate(wgpu::Texture),
|
||||
ScheduleBrowserWork(std::time::Instant),
|
||||
WebCommunicationInitialized,
|
||||
DesktopWrapperMessage(DesktopWrapperMessage),
|
||||
NodeGraphExecutionResult(NodeGraphExecutionResult),
|
||||
CloseWindow,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AppEventScheduler {
|
||||
pub(crate) proxy: winit::event_loop::EventLoopProxy,
|
||||
pub(crate) sender: std::sync::mpsc::Sender<AppEvent>,
|
||||
}
|
||||
|
||||
impl AppEventScheduler {
|
||||
pub(crate) fn schedule(&self, event: AppEvent) {
|
||||
let _ = self.sender.send(event);
|
||||
self.proxy.wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait CreateAppEventSchedulerEventLoopExt {
|
||||
fn create_app_event_scheduler(&self, sender: std::sync::mpsc::Sender<AppEvent>) -> AppEventScheduler;
|
||||
}
|
||||
|
||||
impl CreateAppEventSchedulerEventLoopExt for winit::event_loop::EventLoop {
|
||||
fn create_app_event_scheduler(&self, sender: std::sync::mpsc::Sender<AppEvent>) -> AppEventScheduler {
|
||||
AppEventScheduler { proxy: self.create_proxy(), sender }
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,22 @@
|
||||
use std::process::exit;
|
||||
use std::time::Instant;
|
||||
|
||||
use cef::CefHandler;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
use graphite_desktop_wrapper::WgpuContext;
|
||||
|
||||
pub(crate) mod consts;
|
||||
|
||||
mod app;
|
||||
mod cef;
|
||||
|
||||
mod dirs;
|
||||
mod event;
|
||||
mod native_window;
|
||||
|
||||
mod persist;
|
||||
mod render;
|
||||
|
||||
mod app;
|
||||
use app::WinitApp;
|
||||
|
||||
mod dirs;
|
||||
mod persist;
|
||||
|
||||
use graphite_desktop_wrapper::messages::DesktopWrapperMessage;
|
||||
use graphite_desktop_wrapper::{NodeGraphExecutionResult, WgpuContext};
|
||||
|
||||
pub(crate) enum CustomEvent {
|
||||
UiUpdate(wgpu::Texture),
|
||||
ScheduleBrowserWork(Instant),
|
||||
WebCommunicationInitialized,
|
||||
DesktopWrapperMessage(DesktopWrapperMessage),
|
||||
NodeGraphExecutionResult(NodeGraphExecutionResult),
|
||||
CloseWindow,
|
||||
}
|
||||
use app::App;
|
||||
use cef::CefHandler;
|
||||
use event::CreateAppEventSchedulerEventLoopExt;
|
||||
|
||||
fn main() {
|
||||
tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init();
|
||||
@@ -46,13 +33,18 @@ fn main() {
|
||||
|
||||
let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap();
|
||||
|
||||
let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let (app_event_sender, app_event_receiver) = std::sync::mpsc::channel();
|
||||
let app_event_scheduler = event_loop.create_app_event_scheduler(app_event_sender);
|
||||
|
||||
let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel();
|
||||
|
||||
let cef_handler = cef::CefHandler::new(window_size_receiver, event_loop.create_proxy(), wgpu_context.clone());
|
||||
let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), window_size_receiver);
|
||||
let cef_context = match cef_context_builder.initialize(cef_handler) {
|
||||
Ok(c) => c,
|
||||
Ok(c) => {
|
||||
tracing::info!("CEF initialized successfully");
|
||||
c
|
||||
}
|
||||
Err(cef::InitError::AlreadyRunning) => {
|
||||
tracing::error!("Another instance is already running, Exiting.");
|
||||
exit(0);
|
||||
@@ -71,9 +63,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
tracing::info!("CEF initialized successfully");
|
||||
let mut app = App::new(Box::new(cef_context), window_size_sender, wgpu_context, app_event_receiver, app_event_scheduler);
|
||||
|
||||
let mut winit_app = WinitApp::new(Box::new(cef_context), window_size_sender, wgpu_context, event_loop.create_proxy());
|
||||
|
||||
event_loop.run_app(&mut winit_app).unwrap();
|
||||
event_loop.run_app(&mut app).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
|
||||
use crate::consts::APP_NAME;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
@@ -17,25 +19,31 @@ impl Default for NativeWindowHandle {
|
||||
}
|
||||
impl NativeWindowHandle {
|
||||
#[allow(unused_variables)]
|
||||
pub(super) fn build(&mut self, window: WindowAttributes, event_loop: &ActiveEventLoop) -> WindowAttributes {
|
||||
pub(super) fn build(&mut self, event_loop: &dyn ActiveEventLoop) -> WindowAttributes {
|
||||
let mut window = WindowAttributes::default()
|
||||
.with_title(APP_NAME)
|
||||
.with_min_surface_size(winit::dpi::LogicalSize::new(400, 300))
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(1200, 800))
|
||||
.with_resizable(true);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use crate::consts::{APP_ID, APP_NAME};
|
||||
use winit::platform::wayland::ActiveEventLoopExtWayland;
|
||||
if event_loop.is_wayland() {
|
||||
winit::platform::wayland::WindowAttributesExtWayland::with_name(window, APP_ID, "")
|
||||
use winit::platform::wayland::WindowAttributesWayland;
|
||||
use winit::platform::x11::WindowAttributesX11;
|
||||
window = if event_loop.is_wayland() {
|
||||
let wayland_window = WindowAttributesWayland::default().with_name(APP_ID, "");
|
||||
window.with_platform_attributes(Box::new(wayland_window))
|
||||
} else {
|
||||
winit::platform::x11::WindowAttributesExtX11::with_name(window, APP_ID, APP_NAME)
|
||||
let x11_window = WindowAttributesX11::default().with_name(APP_ID, APP_NAME);
|
||||
window.with_platform_attributes(Box::new(x11_window))
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
window
|
||||
}
|
||||
window
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn setup(&mut self, window: &Window) {
|
||||
pub(crate) fn setup(&mut self, window: &dyn Window) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
*self = NativeWindowHandle::Windows(windows::WindowsNativeWindowHandle::new(window));
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(super) struct WindowsNativeWindowHandle {
|
||||
inner: WindowsNativeWindowHandleInner,
|
||||
}
|
||||
impl WindowsNativeWindowHandle {
|
||||
pub(super) fn new(window: &Window) -> Self {
|
||||
pub(super) fn new(window: &dyn Window) -> Self {
|
||||
let inner = WindowsNativeWindowHandleInner::new(window);
|
||||
WindowsNativeWindowHandle { inner }
|
||||
}
|
||||
@@ -41,7 +41,7 @@ struct WindowsNativeWindowHandleInner {
|
||||
prev_window_message_handler: isize,
|
||||
}
|
||||
impl WindowsNativeWindowHandleInner {
|
||||
fn new(window: &Window) -> WindowsNativeWindowHandleInner {
|
||||
fn new(window: &dyn Window) -> WindowsNativeWindowHandleInner {
|
||||
// Extract Win32 HWND from winit.
|
||||
let hwnd = match window.window_handle().expect("No window handle").as_raw() {
|
||||
RawWindowHandle::Win32(h) => HWND(h.hwnd.get() as *mut std::ffi::c_void),
|
||||
|
||||
@@ -24,8 +24,8 @@ pub(crate) struct GraphicsState {
|
||||
}
|
||||
|
||||
impl GraphicsState {
|
||||
pub(crate) fn new(window: Arc<Window>, context: WgpuContext) -> Self {
|
||||
let size = window.inner_size();
|
||||
pub(crate) fn new(window: Arc<dyn Window>, context: WgpuContext) -> Self {
|
||||
let size = window.surface_size();
|
||||
|
||||
let surface = context.instance.create_surface(window).unwrap();
|
||||
|
||||
@@ -232,7 +232,7 @@ impl GraphicsState {
|
||||
self.bind_overlays_texture(texture);
|
||||
}
|
||||
|
||||
pub(crate) fn render(&mut self, window: &Window) -> Result<(), wgpu::SurfaceError> {
|
||||
pub(crate) fn render(&mut self, window: &dyn Window) -> Result<(), wgpu::SurfaceError> {
|
||||
if let Some(scene) = self.overlays_scene.take() {
|
||||
self.render_overlays(scene);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user