diff --git a/Cargo.lock b/Cargo.lock index 89d9ba1860..5e620f0b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2252,6 +2252,7 @@ dependencies = [ "bytemuck", "cef", "cef-dll-sys", + "clap", "core-foundation", "derivative", "dirs", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index e8e860fb76..586e2470b0 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -43,6 +43,7 @@ rfd = { workspace = true } open = { workspace = true } rand = { workspace = true, features = ["thread_rng"] } serde = { workspace = true } +clap = { workspace = true, features = ["derive"] } # Hardware acceleration dependencies ash = { version = "0.38", optional = true } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 7e0d943fcc..0e2d9d86ca 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -1,4 +1,6 @@ use rfd::AsyncFileDialog; +use std::fs; +use std::path::PathBuf; use std::sync::Arc; use std::sync::mpsc::Receiver; use std::sync::mpsc::Sender; @@ -39,6 +41,7 @@ pub(crate) struct App { web_communication_initialized: bool, web_communication_startup_buffer: Vec>, persistent_data: PersistentData, + launch_documents: Vec, } impl App { @@ -48,6 +51,7 @@ impl App { wgpu_context: WgpuContext, app_event_receiver: Receiver, app_event_scheduler: AppEventScheduler, + launch_documents: Vec, ) -> Self { let rendering_app_event_scheduler = app_event_scheduler.clone(); let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1); @@ -79,6 +83,7 @@ impl App { web_communication_startup_buffer: Vec::new(), persistent_data, native_window: Default::default(), + launch_documents, } } @@ -102,7 +107,7 @@ impl App { let show_dialog = async move { dialog.pick_file().await.map(|f| f.path().to_path_buf()) }; if let Some(path) = futures::executor::block_on(show_dialog) - && let Ok(content) = std::fs::read(&path) + && let Ok(content) = fs::read(&path) { let message = DesktopWrapperMessage::OpenFileDialogResult { path, content, context }; app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)); @@ -135,7 +140,7 @@ impl App { }); } DesktopFrontendMessage::WriteFile { path, content } => { - if let Err(e) = std::fs::write(&path, content) { + if let Err(e) = fs::write(&path, content) { tracing::error!("Failed to write file {}: {}", path.display(), e); } } @@ -197,6 +202,14 @@ impl App { DesktopFrontendMessage::PersistenceUpdateDocumentsList { ids } => { self.persistent_data.set_document_order(ids); } + DesktopFrontendMessage::PersistenceWritePreferences { preferences } => { + self.persistent_data.write_preferences(preferences); + } + DesktopFrontendMessage::PersistenceLoadPreferences => { + let preferences = self.persistent_data.load_preferences(); + let message = DesktopWrapperMessage::LoadPreferences { preferences }; + responses.push(message); + } DesktopFrontendMessage::PersistenceLoadCurrentDocument => { if let Some((id, document)) = self.persistent_data.current_document() { let message = DesktopWrapperMessage::LoadDocument { @@ -232,13 +245,23 @@ impl App { responses.push(message); } } - DesktopFrontendMessage::PersistenceWritePreferences { preferences } => { - self.persistent_data.write_preferences(preferences); - } - DesktopFrontendMessage::PersistenceLoadPreferences => { - let preferences = self.persistent_data.load_preferences(); - let message = DesktopWrapperMessage::LoadPreferences { preferences }; - responses.push(message); + DesktopFrontendMessage::OpenLaunchDocuments => { + if self.launch_documents.is_empty() { + return; + } + let app_event_scheduler = self.app_event_scheduler.clone(); + let launch_documents = std::mem::take(&mut self.launch_documents); + let _ = thread::spawn(move || { + for path in launch_documents { + tracing::info!("Opening file from command line: {}", path.display()); + if let Ok(content) = fs::read(&path) { + let message = DesktopWrapperMessage::OpenFile { path, content }; + app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)); + } else { + tracing::error!("Failed to read file: {}", path.display()); + } + } + }); } } } @@ -389,7 +412,7 @@ impl ApplicationHandler for App { } WindowEvent::DragDropped { paths, .. } => { for path in paths { - match std::fs::read(&path) { + match fs::read(&path) { Ok(content) => { let message = DesktopWrapperMessage::OpenFile { path, content }; self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)); diff --git a/desktop/src/cef/context/builder.rs b/desktop/src/cef/context/builder.rs index a5bbc93c8a..ba54062f64 100644 --- a/desktop/src/cef/context/builder.rs +++ b/desktop/src/cef/context/builder.rs @@ -63,7 +63,7 @@ impl CefContextBuilder { } #[cfg(target_os = "macos")] - pub(crate) fn initialize(self, event_handler: H) -> Result { + pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -77,11 +77,11 @@ impl CefContextBuilder { self.initialize_inner(&event_handler, settings)?; - create_browser(event_handler, instance_dir) + create_browser(event_handler, instance_dir, disable_gpu_acceleration) } #[cfg(not(target_os = "macos"))] - pub(crate) fn initialize(self, event_handler: H) -> Result { + pub(crate) fn initialize(self, event_handler: H, disable_gpu_acceleration: bool) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -94,7 +94,7 @@ impl CefContextBuilder { self.initialize_inner(&event_handler, settings)?; - super::multithreaded::run_on_ui_thread(move || match create_browser(event_handler, instance_dir) { + super::multithreaded::run_on_ui_thread(move || match create_browser(event_handler, instance_dir, disable_gpu_acceleration) { Ok(context) => { super::multithreaded::CONTEXT.with(|b| { *b.borrow_mut() = Some(context); @@ -125,14 +125,21 @@ impl CefContextBuilder { } } -fn create_browser(event_handler: H, instance_dir: PathBuf) -> Result { +fn create_browser(event_handler: H, instance_dir: PathBuf, disable_gpu_acceleration: bool) -> Result { let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone())); let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone())); + #[cfg(feature = "accelerated_paint")] + let use_accelerated_paint = if disable_gpu_acceleration { + false + } else { + crate::cef::platform::should_enable_hardware_acceleration() + }; + let window_info = WindowInfo { windowless_rendering_enabled: 1, #[cfg(feature = "accelerated_paint")] - shared_texture_enabled: if crate::cef::platform::should_enable_hardware_acceleration() { 1 } else { 0 }, + shared_texture_enabled: use_accelerated_paint as i32, ..Default::default() }; diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs new file mode 100644 index 0000000000..4e54bd2477 --- /dev/null +++ b/desktop/src/cli.rs @@ -0,0 +1,9 @@ +#[derive(clap::Parser)] +#[clap(name = "graphite-editor", version)] +pub struct Cli { + #[arg(help = "Files to open on startup")] + pub files: Vec, + + #[arg(long, action = clap::ArgAction::SetTrue, help = "Disable hardware accelerated UI rendering")] + pub disable_ui_acceleration: bool, +} diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 4c15a05d78..430db35648 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -1,3 +1,4 @@ +use clap::Parser; use std::process::exit; use tracing_subscriber::EnvFilter; use winit::event_loop::EventLoop; @@ -6,6 +7,7 @@ pub(crate) mod consts; mod app; mod cef; +mod cli; mod dirs; mod event; mod native_window; @@ -16,6 +18,7 @@ mod gpu_context; use app::App; use cef::CefHandler; +use cli::Cli; use event::CreateAppEventSchedulerEventLoopExt; fn main() { @@ -31,6 +34,8 @@ fn main() { return; } + let cli = Cli::parse(); + let wgpu_context = futures::executor::block_on(gpu_context::create_wgpu_context()); let event_loop = EventLoop::new().unwrap(); @@ -40,7 +45,7 @@ fn main() { let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel(); 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) { + let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) { Ok(c) => { tracing::info!("CEF initialized successfully"); c @@ -63,7 +68,7 @@ fn main() { } }; - let mut app = App::new(Box::new(cef_context), window_size_sender, wgpu_context, app_event_receiver, app_event_scheduler); + let mut app = App::new(Box::new(cef_context), window_size_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files); event_loop.run_app(&mut app).unwrap(); } diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index 0d7a009963..2b1cdb86fa 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -110,6 +110,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::TriggerLoadRestAutoSaveDocuments => { dispatcher.respond(DesktopFrontendMessage::PersistenceLoadRemainingDocuments); } + FrontendMessage::TriggerOpenLaunchDocuments => { + dispatcher.respond(DesktopFrontendMessage::OpenLaunchDocuments); + } FrontendMessage::TriggerSavePreferences { preferences } => { dispatcher.respond(DesktopFrontendMessage::PersistenceWritePreferences { preferences }); } diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index eb55de3239..33704c2548 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -8,6 +8,7 @@ pub use graphite_editor::messages::prelude::PreferencesMessageHandler as Prefere pub enum DesktopFrontendMessage { ToWeb(Vec), + OpenLaunchDocuments, OpenFileDialog { title: String, filters: Vec, diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 91257670b8..637b12425f 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -102,6 +102,7 @@ pub enum FrontendMessage { }, TriggerLoadFirstAutoSaveDocument, TriggerLoadRestAutoSaveDocuments, + TriggerOpenLaunchDocuments, TriggerLoadPreferences, TriggerOpenDocument, TriggerPaste, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index cb852eb3bc..959a5df89a 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -153,6 +153,8 @@ impl MessageHandler> for Portfolio // Tell frontend to finish loading persistent documents responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments); + + responses.add(FrontendMessage::TriggerOpenLaunchDocuments); } PortfolioMessage::DocumentPassMessage { document_id, message } => { if let Some(document) = self.documents.get_mut(&document_id) {