From 03d5dcbef0fb87ddae282d4c3c0d78dc115ff51c Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Sat, 16 Aug 2025 21:18:58 -0300 Subject: [PATCH 01/12] Open document from cli and add some files to .gitignore --- .gitignore | 1 + desktop/src/app.rs | 52 ++++++++++++++++++++++++++++++++++----------- desktop/src/cef.rs | 2 +- frontend/.gitignore | 2 ++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 3fa518041e..f0bfe75b36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target/ +node_modules/ *.spv *.exrc perf.data* diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 803a2f0a3a..e2ab5d8a72 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -12,6 +12,7 @@ use graphene_std::raster::Image; use graphite_editor::application::Editor; use graphite_editor::consts::DEFAULT_DOCUMENT_NAME; use graphite_editor::messages::prelude::*; +use std::env; use std::fs; use std::sync::Arc; use std::sync::mpsc::Sender; @@ -221,6 +222,32 @@ impl ApplicationHandler for WinitApp { self.dispatch_message(message); } CustomEvent::MessageReceived(message) => { + if let Message::Portfolio(PortfolioMessage::Init) = &message { + let args: Vec = env::args().collect(); + + // Try to open a file passed as the first command line argument + if args.len() > 1 { + let event_loop_proxy = self.event_loop_proxy.clone(); + let path = std::path::PathBuf::from(&args[1]); + + if !path.exists() { + tracing::warn!("The first argument is not a valid path or doesn't exist: {}", path.display()); + return; + } + + let name = path.file_stem().and_then(|s| s.to_str()).map(|s| s.to_string()); + + // Load the file in a separate thread to avoid blocking the main thread + let _ = thread::spawn(move || { + let Some(content) = load_string(&path) else { return }; + let message = PortfolioMessage::OpenDocumentFile { + document_name: name.unwrap_or(DEFAULT_DOCUMENT_NAME.to_string()), + document_serialized_content: content, + }; + let _ = event_loop_proxy.send_event(CustomEvent::DispatchMessage(message.into())); + }); + } + } if let Message::InputPreprocessor(_) = &message { if let Some(window) = &self.window { window.request_redraw(); @@ -276,18 +303,6 @@ impl ApplicationHandler for WinitApp { // Fine to early return since we don't need to do cef work in this case return; }; - let load_string = |path: &std::path::PathBuf| { - let Ok(content) = fs::read_to_string(path) else { - tracing::error!("Failed to read file: {}", path.display()); - return None; - }; - - if content.is_empty() { - tracing::warn!("Dropped file is empty: {}", path.display()); - return None; - } - Some(content) - }; // TODO: Consider moving this logic to the editor so we have one message to load data which is then demultiplexed in the portfolio message handler match extension { "graphite" => { @@ -368,3 +383,16 @@ impl ApplicationHandler for WinitApp { self.cef_context.work(); } } + +fn load_string(path: &std::path::PathBuf) -> Option { + let Ok(content) = fs::read_to_string(path) else { + tracing::error!("Failed to read file: {}", path.display()); + return None; + }; + + if content.is_empty() { + tracing::warn!("Dropped file is empty: {}", path.display()); + return None; + } + Some(content) +} diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index af1e7ec73d..025259ce49 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -18,7 +18,7 @@ use winit::event_loop::EventLoopProxy; pub(crate) trait CefEventHandler: Clone { fn window_size(&self) -> WindowSize; fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>); - /// Scheudule the main event loop to run the cef event loop after the timeout + /// Schedule 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 receive_web_message(&self, message: &[u8]); diff --git a/frontend/.gitignore b/frontend/.gitignore index 12d5b4e4a3..d95a209f67 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -2,3 +2,5 @@ node_modules/ wasm/pkg/ public/build/ dist/ +CMakeFiles/ +CMakeCache.txt From ad81899258741eb8da32e02e35312cc6d7d4ecc1 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:34:09 -0300 Subject: [PATCH 02/12] Revert .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index f0bfe75b36..3fa518041e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ target/ -node_modules/ *.spv *.exrc perf.data* From ab276c9dbbf8f36206bfeccf4f3d32c5fbb83048 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:34:46 -0300 Subject: [PATCH 03/12] Revert .gitignore --- frontend/.gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/.gitignore b/frontend/.gitignore index d95a209f67..12d5b4e4a3 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -2,5 +2,3 @@ node_modules/ wasm/pkg/ public/build/ dist/ -CMakeFiles/ -CMakeCache.txt From a3963fc7254659cb1c67064b7209ca9871f625f4 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:24:17 -0300 Subject: [PATCH 04/12] Implement cli properly --- .vscode/launch.json | 55 ++++++++++--------- .vscode/tasks.json | 28 ++++++++++ Cargo.lock | 1 + desktop/Cargo.toml | 1 + desktop/src/app.rs | 40 ++++++++++---- desktop/src/cli.rs | 22 ++++++++ desktop/src/main.rs | 4 ++ .../wrapper/src/intercept_editor_message.rs | 5 ++ desktop/wrapper/src/messages.rs | 1 + 9 files changed, 119 insertions(+), 38 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 desktop/src/cli.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index df57674a13..04b2e6496e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,43 +6,46 @@ "configurations": [ { "type": "lldb", + "request": "attach", + "name": "Attach", + "program": "${workspaceFolder}/target/debug/graphite-desktop.exe" + }, + { + "type": "cppvsdbg", "request": "launch", "name": "Graphite debug executable", - "cargo": { - "args": [ - "build", - "--bin=graphite", - "--package=graphite", - ], - "filter": { - "name": "graphite", - "kind": "bin", - }, - }, + "program": "${workspaceFolder}/target/debug/graphite-desktop.exe", "args": [], - "cwd": "${workspaceFolder}", - "env": { - "RUST_LOG": "error", - }, + "cwd": "${workspaceFolder}/frontend", + "preLaunchTask": "Build desktop", + "environment": [ + { + "name": "Path", + "value": "${env:Path};${workspaceFolder}/target/debug/build/cef-dll-sys-64de2d8cf2651480/out/build/libcef_dll_wrapper;${workspaceFolder}/target/debug/build/cef-dll-sys-64de2d8cf2651480/out/cef_windows_x86_64;${workspaceFolder}/target/debug/build/ring-3cde4330c4f9f08b/out;${workspaceFolder}/target/debug/build/ring-d1ae505507179bc0/out;${workspaceFolder}/target/debug/deps;${workspaceFolder}/target/debug;${env:USERPROFILE}\\.cargo\\bin" + } + ] + }, + { + "type": "cppvsdbg", + "request": "launch", + "name": "Debug Graphite Desktop (VS)", + "program": "${workspaceFolder}/target/debug/graphite-desktop.exe", + "args": [], + "cwd": "${workspaceFolder}/frontend" }, { "type": "lldb", "request": "launch", "name": "Debug unit tests in executable 'graphite'", "cargo": { - "args": [ - "test", - "--no-run", - "--bin=graphite", - "--package=graphite", - ], + "args": ["test", "--no-run", "--bin=graphite", "--package=graphite"], "filter": { "name": "graphite", - "kind": "bin", - }, + "kind": "bin" + } }, "args": [], - "cwd": "${workspaceFolder}", - }, - ], + "cwd": "${workspaceFolder}" + } + ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..b776b30100 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "npm", + "script": "start-desktop", + "problemMatcher": [], + "label": "npm: start-desktop", + "detail": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "command": "cargo build -p graphite-desktop", + "problemMatcher": [], + "label": "Build desktop", + "group": { + "kind": "build", + "isDefault": false + }, + "options": { + "cwd": "${workspaceFolder}" + } + } + ] +} diff --git a/Cargo.lock b/Cargo.lock index 98a7c9043d..cb40ba93d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2266,6 +2266,7 @@ dependencies = [ "bytemuck", "cef", "cef-dll-sys", + "clap", "core-foundation", "derivative", "dirs", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 43fb5fc94e..30e1d230eb 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 11be0cdc45..7fe36f475b 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -1,5 +1,6 @@ use crate::CustomEvent; use crate::cef::WindowSize; +use crate::cli::get_cli; use crate::consts::{APP_NAME, CEF_MESSAGE_LOOP_MAX_ITERATIONS}; use crate::persist::PersistentData; use crate::render::GraphicsState; @@ -86,6 +87,21 @@ impl WinitApp { }; self.send_or_queue_web_message(bytes); } + DesktopFrontendMessage::Init => { + tracing::info!("Initializing application..."); + let cli = get_cli(); + if let Some(paths) = cli.files.clone() { + for path in paths { + tracing::info!("Opening file from command line: {}", path.display()); + if let Ok(content) = std::fs::read(&path) { + let message = DesktopWrapperMessage::OpenFile { path, content }; + self.dispatch_desktop_wrapper_message(message); + } else { + tracing::error!("Failed to read file: {}", path.display()); + } + } + } + } DesktopFrontendMessage::OpenFileDialog { title, filters, context } => { let event_loop_proxy = self.event_loop_proxy.clone(); let _ = thread::spawn(move || { @@ -408,15 +424,15 @@ impl ApplicationHandler for WinitApp { } } -fn load_string(path: &std::path::PathBuf) -> Option { - let Ok(content) = fs::read_to_string(path) else { - tracing::error!("Failed to read file: {}", path.display()); - return None; - }; - - if content.is_empty() { - tracing::warn!("Dropped file is empty: {}", path.display()); - return None; - } - Some(content) -} +// fn load_string(path: &std::path::PathBuf) -> Option { +// let Ok(content) = fs::read_to_string(path) else { +// tracing::error!("Failed to read file: {}", path.display()); +// return None; +// }; + +// if content.is_empty() { +// tracing::warn!("Dropped file is empty: {}", path.display()); +// return None; +// } +// Some(content) +// } diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs new file mode 100644 index 0000000000..0f64c938f3 --- /dev/null +++ b/desktop/src/cli.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; +use std::sync::OnceLock; + +use clap::Parser; + +#[derive(Debug, Parser)] +// #[command(version, about, long_about = None)] +#[clap(name = "graphite-cli", version)] +pub struct Cli { + pub files: Option>, +} + +static CLI: OnceLock = OnceLock::new(); + +pub fn init_cli() -> &'static Cli { + CLI.set(Cli::parse()).ok(); + CLI.get().expect("CLI should be initialized") +} + +pub fn get_cli() -> &'static Cli { + CLI.get().expect("CLI should be initialized") +} diff --git a/desktop/src/main.rs b/desktop/src/main.rs index d6aa4414f5..37862cc2c7 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -16,6 +16,8 @@ mod render; mod app; use app::WinitApp; +mod cli; + mod dirs; mod persist; @@ -44,6 +46,8 @@ fn main() { return; } + cli::init_cli(); + let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap(); let event_loop = EventLoop::::with_user_event().build().unwrap(); diff --git a/desktop/wrapper/src/intercept_editor_message.rs b/desktop/wrapper/src/intercept_editor_message.rs index 4fc869f8c5..4f9d86edbf 100644 --- a/desktop/wrapper/src/intercept_editor_message.rs +++ b/desktop/wrapper/src/intercept_editor_message.rs @@ -1,4 +1,5 @@ use graphite_editor::messages::prelude::InputPreprocessorMessage; +use graphite_editor::messages::tool::tool_messages::tool_prelude::PortfolioMessage; use super::DesktopWrapperMessageDispatcher; use super::messages::{DesktopFrontendMessage, EditorMessage}; @@ -18,6 +19,10 @@ pub(super) fn intercept_editor_message(dispatcher: &mut DesktopWrapperMessageDis } Some(EditorMessage::InputPreprocessor(message)) } + EditorMessage::Portfolio(PortfolioMessage::Init) => { + dispatcher.respond(DesktopFrontendMessage::Init); + Some(EditorMessage::Portfolio(PortfolioMessage::Init)) + } m => Some(m), } } diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index 8804944e0e..4873303949 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), + Init, OpenFileDialog { title: String, filters: Vec, From daea5b617cd9ae316f88916cd84b0c9367bc9d30 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:22:49 -0300 Subject: [PATCH 05/12] Add ui-accelerated-painting option --- desktop/src/cef/platform.rs | 111 ++++++++++++++++++++---------------- desktop/src/cli.rs | 13 ++++- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index 7df5d5f982..c49b55e63a 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,59 +1,70 @@ +use crate::cli::{get_cli, UIAceleratedPainting}; + #[cfg(feature = "accelerated_paint")] pub fn should_enable_hardware_acceleration() -> bool { - #[cfg(target_os = "linux")] - { - // Check if running on Wayland or X11 - let has_wayland = std::env::var("WAYLAND_DISPLAY") - .ok() - .filter(|var| !var.is_empty()) - .or_else(|| std::env::var("WAYLAND_SOCKET").ok()) - .filter(|var| !var.is_empty()) - .is_some(); - - let has_x11 = std::env::var("DISPLAY").ok().filter(|var| !var.is_empty()).is_some(); - - if !has_wayland && !has_x11 { - tracing::warn!("No display server detected, disabling hardware acceleration"); - return false; - } + match get_cli().ui_accelerated_painting { + // User explicitly enabled + UIAceleratedPainting::Yes => true, + // User explicitly disabled + UIAceleratedPainting::No => false, + // Auto-detect + UIAceleratedPainting::Auto => { + #[cfg(target_os = "linux")] + { + // Check if running on Wayland or X11 + let has_wayland = std::env::var("WAYLAND_DISPLAY") + .ok() + .filter(|var| !var.is_empty()) + .or_else(|| std::env::var("WAYLAND_SOCKET").ok()) + .filter(|var| !var.is_empty()) + .is_some(); - // Check for NVIDIA proprietary driver (known to have issues) - if let Ok(driver_info) = std::fs::read_to_string("/proc/driver/nvidia/version") { - if driver_info.contains("NVIDIA") { - tracing::warn!("NVIDIA proprietary driver detected, hardware acceleration may be unstable"); - // Still return true but with warning - } - } + let has_x11 = std::env::var("DISPLAY").ok().filter(|var| !var.is_empty()).is_some(); - // Check for basic GPU capabilities - if has_wayland { - tracing::info!("Wayland detected, enabling hardware acceleration"); - true - } else if has_x11 { - tracing::info!("X11 detected, enabling hardware acceleration"); - true - } else { - false - } - } + if !has_wayland && !has_x11 { + tracing::warn!("No display server detected, disabling hardware acceleration"); + return false; + } - #[cfg(target_os = "windows")] - { - // Windows generally has good D3D11 support - tracing::info!("Windows detected, enabling hardware acceleration"); - true - } + // Check for NVIDIA proprietary driver (known to have issues) + if let Ok(driver_info) = std::fs::read_to_string("/proc/driver/nvidia/version") { + if driver_info.contains("NVIDIA") { + tracing::warn!("NVIDIA proprietary driver detected, hardware acceleration may be unstable"); + // Still return true but with warning + } + } - #[cfg(target_os = "macos")] - { - // macOS has good Metal/IOSurface support - tracing::info!("macOS detected, enabling hardware acceleration"); - true - } + // Check for basic GPU capabilities + if has_wayland { + tracing::info!("Wayland detected, enabling hardware acceleration"); + true + } else if has_x11 { + tracing::info!("X11 detected, enabling hardware acceleration"); + true + } else { + false + } + } + + #[cfg(target_os = "windows")] + { + // Windows generally has good D3D11 support + tracing::info!("Windows detected, enabling hardware acceleration"); + true + } - #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] - { - tracing::warn!("Unsupported platform for hardware acceleration"); - false + #[cfg(target_os = "macos")] + { + // macOS has good Metal/IOSurface support + tracing::info!("macOS detected, enabling hardware acceleration"); + true + } + + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + { + tracing::warn!("Unsupported platform for hardware acceleration"); + false + } + } } } diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index 0f64c938f3..0c5b689ae5 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -1,13 +1,22 @@ use std::path::PathBuf; use std::sync::OnceLock; -use clap::Parser; +use clap::{Parser, ValueEnum}; + +#[derive(Debug, Clone, ValueEnum)] +pub enum UIAceleratedPainting { + Auto, + Yes, + No, +} #[derive(Debug, Parser)] -// #[command(version, about, long_about = None)] #[clap(name = "graphite-cli", version)] pub struct Cli { pub files: Option>, + + #[arg(long, value_enum, default_value_t = UIAceleratedPainting::Auto, help = "Enable UI accelerated painting using GPU")] + pub ui_accelerated_painting: UIAceleratedPainting, } static CLI: OnceLock = OnceLock::new(); From 9b41397eef409c067657041d86c0f167106f7d45 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:23:40 -0300 Subject: [PATCH 06/12] Format code --- desktop/src/cef/platform.rs | 2 +- desktop/src/cli.rs | 6 +++--- desktop/src/main.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index c49b55e63a..9d48917476 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,4 +1,4 @@ -use crate::cli::{get_cli, UIAceleratedPainting}; +use crate::cli::{UIAceleratedPainting, get_cli}; #[cfg(feature = "accelerated_paint")] pub fn should_enable_hardware_acceleration() -> bool { diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index 0c5b689ae5..0b0673cdd0 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -5,9 +5,9 @@ use clap::{Parser, ValueEnum}; #[derive(Debug, Clone, ValueEnum)] pub enum UIAceleratedPainting { - Auto, - Yes, - No, + Auto, + Yes, + No, } #[derive(Debug, Parser)] diff --git a/desktop/src/main.rs b/desktop/src/main.rs index a1ad641d8e..cf48a718b5 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -7,8 +7,8 @@ use graphite_desktop_wrapper::WgpuContext; pub(crate) mod consts; mod app; -mod cli; mod cef; +mod cli; mod dirs; mod event; mod native_window; From f8ebda3b5a61da4c7eed06ab163640d9b6983f33 Mon Sep 17 00:00:00 2001 From: Starklosch <45690579+Starklosch@users.noreply.github.com> Date: Wed, 17 Sep 2025 22:27:41 -0300 Subject: [PATCH 07/12] Revert changes in .vscode folder --- .vscode/launch.json | 55 +++++++++++++++++++++------------------------ .vscode/tasks.json | 28 ----------------------- 2 files changed, 26 insertions(+), 57 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 04b2e6496e..df57674a13 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,46 +6,43 @@ "configurations": [ { "type": "lldb", - "request": "attach", - "name": "Attach", - "program": "${workspaceFolder}/target/debug/graphite-desktop.exe" - }, - { - "type": "cppvsdbg", "request": "launch", "name": "Graphite debug executable", - "program": "${workspaceFolder}/target/debug/graphite-desktop.exe", - "args": [], - "cwd": "${workspaceFolder}/frontend", - "preLaunchTask": "Build desktop", - "environment": [ - { - "name": "Path", - "value": "${env:Path};${workspaceFolder}/target/debug/build/cef-dll-sys-64de2d8cf2651480/out/build/libcef_dll_wrapper;${workspaceFolder}/target/debug/build/cef-dll-sys-64de2d8cf2651480/out/cef_windows_x86_64;${workspaceFolder}/target/debug/build/ring-3cde4330c4f9f08b/out;${workspaceFolder}/target/debug/build/ring-d1ae505507179bc0/out;${workspaceFolder}/target/debug/deps;${workspaceFolder}/target/debug;${env:USERPROFILE}\\.cargo\\bin" - } - ] - }, - { - "type": "cppvsdbg", - "request": "launch", - "name": "Debug Graphite Desktop (VS)", - "program": "${workspaceFolder}/target/debug/graphite-desktop.exe", + "cargo": { + "args": [ + "build", + "--bin=graphite", + "--package=graphite", + ], + "filter": { + "name": "graphite", + "kind": "bin", + }, + }, "args": [], - "cwd": "${workspaceFolder}/frontend" + "cwd": "${workspaceFolder}", + "env": { + "RUST_LOG": "error", + }, }, { "type": "lldb", "request": "launch", "name": "Debug unit tests in executable 'graphite'", "cargo": { - "args": ["test", "--no-run", "--bin=graphite", "--package=graphite"], + "args": [ + "test", + "--no-run", + "--bin=graphite", + "--package=graphite", + ], "filter": { "name": "graphite", - "kind": "bin" - } + "kind": "bin", + }, }, "args": [], - "cwd": "${workspaceFolder}" - } - ] + "cwd": "${workspaceFolder}", + }, + ], } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index b776b30100..0000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "tasks": [ - { - "type": "npm", - "script": "start-desktop", - "problemMatcher": [], - "label": "npm: start-desktop", - "detail": "cd frontend && npm run build-native-dev && cargo run -p graphite-desktop", - "group": { - "kind": "build", - "isDefault": true - } - }, - { - "type": "shell", - "command": "cargo build -p graphite-desktop", - "problemMatcher": [], - "label": "Build desktop", - "group": { - "kind": "build", - "isDefault": false - }, - "options": { - "cwd": "${workspaceFolder}" - } - } - ] -} From bae94240abed5634de35c0b1f54677aae6b322e6 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Mon, 22 Sep 2025 00:33:18 -0300 Subject: [PATCH 08/12] Don't use global variables for cli --- desktop/src/app.rs | 7 ++++--- desktop/src/cef/context/builder.rs | 17 +++++++++++------ desktop/src/cef/platform.rs | 6 +++--- desktop/src/cli.rs | 12 ------------ desktop/src/main.rs | 9 ++++++--- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index ded45f5d31..4b44618b89 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -14,7 +14,6 @@ use winit::window::Window; use winit::window::WindowId; use crate::cef; -use crate::cli::get_cli; use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS; use crate::event::{AppEvent, AppEventScheduler}; use crate::native_window; @@ -40,6 +39,7 @@ pub(crate) struct App { web_communication_initialized: bool, web_communication_startup_buffer: Vec>, persistent_data: PersistentData, + cli_files: Option>, } impl App { @@ -49,6 +49,7 @@ impl App { wgpu_context: WgpuContext, app_event_receiver: Receiver, app_event_scheduler: AppEventScheduler, + cli_files: Option>, ) -> Self { let rendering_app_event_scheduler = app_event_scheduler.clone(); let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1); @@ -80,6 +81,7 @@ impl App { web_communication_startup_buffer: Vec::new(), persistent_data, native_window: Default::default(), + cli_files, } } @@ -94,8 +96,7 @@ impl App { } DesktopFrontendMessage::Init => { tracing::info!("Initializing application..."); - let cli = get_cli(); - if let Some(paths) = cli.files.clone() { + if let Some(paths) = self.cli_files.clone() { for path in paths { tracing::info!("Opening file from command line: {}", path.display()); if let Ok(content) = std::fs::read(&path) { diff --git a/desktop/src/cef/context/builder.rs b/desktop/src/cef/context/builder.rs index a5bbc93c8a..373dd65b71 100644 --- a/desktop/src/cef/context/builder.rs +++ b/desktop/src/cef/context/builder.rs @@ -14,6 +14,7 @@ use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME}; use crate::cef::dirs::create_instance_dir; use crate::cef::input::InputState; use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl}; +use crate::cli::UIAceleratedPainting; pub(crate) struct CefContextBuilder { pub(crate) args: Args, @@ -63,7 +64,7 @@ impl CefContextBuilder { } #[cfg(target_os = "macos")] - pub(crate) fn initialize(self, event_handler: H) -> Result { + pub(crate) fn initialize(self, event_handler: H, acceleration_preference: UIAceleratedPainting) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -77,11 +78,11 @@ impl CefContextBuilder { self.initialize_inner(&event_handler, settings)?; - create_browser(event_handler, instance_dir) + create_browser(event_handler, instance_dir, acceleration_preference) } #[cfg(not(target_os = "macos"))] - pub(crate) fn initialize(self, event_handler: H) -> Result { + pub(crate) fn initialize(self, event_handler: H, acceleration_preference: UIAceleratedPainting) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -94,7 +95,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, acceleration_preference) { Ok(context) => { super::multithreaded::CONTEXT.with(|b| { *b.borrow_mut() = Some(context); @@ -125,14 +126,18 @@ impl CefContextBuilder { } } -fn create_browser(event_handler: H, instance_dir: PathBuf) -> Result { +fn create_browser(event_handler: H, instance_dir: PathBuf, acceleration_preference: UIAceleratedPainting) -> Result { let render_handler = RenderHandler::new(RenderHandlerImpl::new(event_handler.clone())); let mut client = Client::new(BrowserProcessClientImpl::new(render_handler, event_handler.clone())); 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: if crate::cef::platform::should_enable_hardware_acceleration(acceleration_preference) { + 1 + } else { + 0 + }, ..Default::default() }; diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index 9d48917476..5d06002a8f 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,8 +1,8 @@ -use crate::cli::{UIAceleratedPainting, get_cli}; +use crate::cli::UIAceleratedPainting; #[cfg(feature = "accelerated_paint")] -pub fn should_enable_hardware_acceleration() -> bool { - match get_cli().ui_accelerated_painting { +pub fn should_enable_hardware_acceleration(preference: UIAceleratedPainting) -> bool { + match preference { // User explicitly enabled UIAceleratedPainting::Yes => true, // User explicitly disabled diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index 0b0673cdd0..a32f32c7eb 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::sync::OnceLock; use clap::{Parser, ValueEnum}; @@ -18,14 +17,3 @@ pub struct Cli { #[arg(long, value_enum, default_value_t = UIAceleratedPainting::Auto, help = "Enable UI accelerated painting using GPU")] pub ui_accelerated_painting: UIAceleratedPainting, } - -static CLI: OnceLock = OnceLock::new(); - -pub fn init_cli() -> &'static Cli { - CLI.set(Cli::parse()).ok(); - CLI.get().expect("CLI should be initialized") -} - -pub fn get_cli() -> &'static Cli { - CLI.get().expect("CLI should be initialized") -} diff --git a/desktop/src/main.rs b/desktop/src/main.rs index cf48a718b5..447ff17343 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; @@ -19,6 +20,8 @@ use app::App; use cef::CefHandler; use event::CreateAppEventSchedulerEventLoopExt; +use crate::cli::Cli; + fn main() { tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init(); @@ -32,7 +35,7 @@ fn main() { return; } - cli::init_cli(); + let cli = Cli::parse(); let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap(); @@ -43,7 +46,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.ui_accelerated_painting) { Ok(c) => { tracing::info!("CEF initialized successfully"); c @@ -66,7 +69,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(); } From de3de8dc784fc3197d85887319c16b51c7afe2d8 Mon Sep 17 00:00:00 2001 From: Mateo <45690579+c-mateo@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:47:26 -0300 Subject: [PATCH 09/12] Apply suggested changes --- desktop/src/app.rs | 22 ++-- desktop/src/cef/context/builder.rs | 17 +-- desktop/src/cef/platform.rs | 111 +++++++++--------- desktop/src/cli.rs | 15 +-- desktop/src/main.rs | 2 +- .../wrapper/src/intercept_editor_message.rs | 5 - .../wrapper/src/intercept_frontend_message.rs | 3 + desktop/wrapper/src/messages.rs | 2 +- .../src/messages/frontend/frontend_message.rs | 2 + .../portfolio/portfolio_message_handler.rs | 2 + 10 files changed, 84 insertions(+), 97 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 770361b946..49be15409c 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -94,18 +94,20 @@ impl App { }; self.send_or_queue_web_message(bytes); } - DesktopFrontendMessage::Init => { - tracing::info!("Initializing application..."); + DesktopFrontendMessage::OpenDocumentsSuppliedAtStartup => { + let app_event_scheduler = self.app_event_scheduler.clone(); if let Some(paths) = self.cli_files.clone() { - for path in paths { - tracing::info!("Opening file from command line: {}", path.display()); - if let Ok(content) = std::fs::read(&path) { - let message = DesktopWrapperMessage::OpenFile { path, content }; - self.dispatch_desktop_wrapper_message(message); - } else { - tracing::error!("Failed to read file: {}", path.display()); + let _ = thread::spawn(move || { + for path in paths { + tracing::info!("Opening file from command line: {}", path.display()); + if let Ok(content) = std::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()); + } } - } + }); } } DesktopFrontendMessage::OpenFileDialog { title, filters, context } => { diff --git a/desktop/src/cef/context/builder.rs b/desktop/src/cef/context/builder.rs index 373dd65b71..833ecde6bd 100644 --- a/desktop/src/cef/context/builder.rs +++ b/desktop/src/cef/context/builder.rs @@ -14,7 +14,6 @@ use crate::cef::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME}; use crate::cef::dirs::create_instance_dir; use crate::cef::input::InputState; use crate::cef::internal::{BrowserProcessAppImpl, BrowserProcessClientImpl, RenderHandlerImpl, RenderProcessAppImpl, SchemeHandlerFactoryImpl}; -use crate::cli::UIAceleratedPainting; pub(crate) struct CefContextBuilder { pub(crate) args: Args, @@ -64,7 +63,7 @@ impl CefContextBuilder { } #[cfg(target_os = "macos")] - pub(crate) fn initialize(self, event_handler: H, acceleration_preference: UIAceleratedPainting) -> Result { + pub(crate) fn initialize(self, event_handler: H, disable_acceleration: bool) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -78,11 +77,11 @@ impl CefContextBuilder { self.initialize_inner(&event_handler, settings)?; - create_browser(event_handler, instance_dir, acceleration_preference) + create_browser(event_handler, instance_dir, disable_acceleration) } #[cfg(not(target_os = "macos"))] - pub(crate) fn initialize(self, event_handler: H, acceleration_preference: UIAceleratedPainting) -> Result { + pub(crate) fn initialize(self, event_handler: H, disable_acceleration: bool) -> Result { let instance_dir = create_instance_dir(); let settings = Settings { @@ -95,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, acceleration_preference) { + super::multithreaded::run_on_ui_thread(move || match create_browser(event_handler, instance_dir, disable_acceleration) { Ok(context) => { super::multithreaded::CONTEXT.with(|b| { *b.borrow_mut() = Some(context); @@ -126,18 +125,14 @@ impl CefContextBuilder { } } -fn create_browser(event_handler: H, instance_dir: PathBuf, acceleration_preference: UIAceleratedPainting) -> Result { +fn create_browser(event_handler: H, instance_dir: PathBuf, disable_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())); let window_info = WindowInfo { windowless_rendering_enabled: 1, #[cfg(feature = "accelerated_paint")] - shared_texture_enabled: if crate::cef::platform::should_enable_hardware_acceleration(acceleration_preference) { - 1 - } else { - 0 - }, + shared_texture_enabled: if crate::cef::platform::should_enable_hardware_acceleration(disable_acceleration) { 1 } else { 0 }, ..Default::default() }; diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index 5d06002a8f..763b9952eb 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,70 +1,65 @@ -use crate::cli::UIAceleratedPainting; - #[cfg(feature = "accelerated_paint")] -pub fn should_enable_hardware_acceleration(preference: UIAceleratedPainting) -> bool { - match preference { - // User explicitly enabled - UIAceleratedPainting::Yes => true, +pub fn should_enable_hardware_acceleration(user_disabled: bool) -> bool { + if user_disabled { // User explicitly disabled - UIAceleratedPainting::No => false, - // Auto-detect - UIAceleratedPainting::Auto => { - #[cfg(target_os = "linux")] - { - // Check if running on Wayland or X11 - let has_wayland = std::env::var("WAYLAND_DISPLAY") - .ok() - .filter(|var| !var.is_empty()) - .or_else(|| std::env::var("WAYLAND_SOCKET").ok()) - .filter(|var| !var.is_empty()) - .is_some(); + return false; + } - let has_x11 = std::env::var("DISPLAY").ok().filter(|var| !var.is_empty()).is_some(); + // Auto-detect + #[cfg(target_os = "linux")] + { + // Check if running on Wayland or X11 + let has_wayland = std::env::var("WAYLAND_DISPLAY") + .ok() + .filter(|var| !var.is_empty()) + .or_else(|| std::env::var("WAYLAND_SOCKET").ok()) + .filter(|var| !var.is_empty()) + .is_some(); - if !has_wayland && !has_x11 { - tracing::warn!("No display server detected, disabling hardware acceleration"); - return false; - } + let has_x11 = std::env::var("DISPLAY").ok().filter(|var| !var.is_empty()).is_some(); - // Check for NVIDIA proprietary driver (known to have issues) - if let Ok(driver_info) = std::fs::read_to_string("/proc/driver/nvidia/version") { - if driver_info.contains("NVIDIA") { - tracing::warn!("NVIDIA proprietary driver detected, hardware acceleration may be unstable"); - // Still return true but with warning - } - } + if !has_wayland && !has_x11 { + tracing::warn!("No display server detected, disabling hardware acceleration"); + return false; + } - // Check for basic GPU capabilities - if has_wayland { - tracing::info!("Wayland detected, enabling hardware acceleration"); - true - } else if has_x11 { - tracing::info!("X11 detected, enabling hardware acceleration"); - true - } else { - false - } + // Check for NVIDIA proprietary driver (known to have issues) + if let Ok(driver_info) = std::fs::read_to_string("/proc/driver/nvidia/version") { + if driver_info.contains("NVIDIA") { + tracing::warn!("NVIDIA proprietary driver detected, hardware acceleration may be unstable"); + // Still return true but with warning } + } - #[cfg(target_os = "windows")] - { - // Windows generally has good D3D11 support - tracing::info!("Windows detected, enabling hardware acceleration"); - true - } + // Check for basic GPU capabilities + if has_wayland { + tracing::info!("Wayland detected, enabling hardware acceleration"); + true + } else if has_x11 { + tracing::info!("X11 detected, enabling hardware acceleration"); + true + } else { + false + } + } - #[cfg(target_os = "macos")] - { - // macOS has good Metal/IOSurface support - tracing::info!("macOS detected, enabling hardware acceleration"); - true - } + #[cfg(target_os = "windows")] + { + // Windows generally has good D3D11 support + tracing::info!("Windows detected, enabling hardware acceleration"); + true + } - #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] - { - tracing::warn!("Unsupported platform for hardware acceleration"); - false - } - } + #[cfg(target_os = "macos")] + { + // macOS has good Metal/IOSurface support + tracing::info!("macOS detected, enabling hardware acceleration"); + true + } + + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + { + tracing::warn!("Unsupported platform for hardware acceleration"); + false } } diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index a32f32c7eb..5e1479d60e 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -1,19 +1,12 @@ use std::path::PathBuf; -use clap::{Parser, ValueEnum}; - -#[derive(Debug, Clone, ValueEnum)] -pub enum UIAceleratedPainting { - Auto, - Yes, - No, -} - +use clap::Parser; #[derive(Debug, Parser)] #[clap(name = "graphite-cli", version)] pub struct Cli { + #[arg(help = "Files to open on startup")] pub files: Option>, - #[arg(long, value_enum, default_value_t = UIAceleratedPainting::Auto, help = "Enable UI accelerated painting using GPU")] - pub ui_accelerated_painting: UIAceleratedPainting, + #[arg(long, action = clap::ArgAction::SetTrue, help = "Disable UI accelerated painting using GPU")] + pub disable_ui_acceleration: Option, } diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 022fbed83a..997f7360b4 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -46,7 +46,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, cli.ui_accelerated_painting) { + let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration.unwrap_or(false)) { Ok(c) => { tracing::info!("CEF initialized successfully"); c diff --git a/desktop/wrapper/src/intercept_editor_message.rs b/desktop/wrapper/src/intercept_editor_message.rs index 4f9d86edbf..4fc869f8c5 100644 --- a/desktop/wrapper/src/intercept_editor_message.rs +++ b/desktop/wrapper/src/intercept_editor_message.rs @@ -1,5 +1,4 @@ use graphite_editor::messages::prelude::InputPreprocessorMessage; -use graphite_editor::messages::tool::tool_messages::tool_prelude::PortfolioMessage; use super::DesktopWrapperMessageDispatcher; use super::messages::{DesktopFrontendMessage, EditorMessage}; @@ -19,10 +18,6 @@ pub(super) fn intercept_editor_message(dispatcher: &mut DesktopWrapperMessageDis } Some(EditorMessage::InputPreprocessor(message)) } - EditorMessage::Portfolio(PortfolioMessage::Init) => { - dispatcher.respond(DesktopFrontendMessage::Init); - Some(EditorMessage::Portfolio(PortfolioMessage::Init)) - } m => Some(m), } } diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index 0d7a009963..5197ebf2b4 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -116,6 +116,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::TriggerLoadPreferences => { dispatcher.respond(DesktopFrontendMessage::PersistenceLoadPreferences); } + FrontendMessage::OpenDocumentsSuppliedAtStartup => { + dispatcher.respond(DesktopFrontendMessage::OpenDocumentsSuppliedAtStartup); + } m => return Some(m), } None diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index bea58806ba..1edf798cb2 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -8,7 +8,7 @@ pub use graphite_editor::messages::prelude::PreferencesMessageHandler as Prefere pub enum DesktopFrontendMessage { ToWeb(Vec), - Init, + OpenDocumentsSuppliedAtStartup, 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..480443bc7d 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -52,6 +52,8 @@ pub enum FrontendMessage { }, DisplayRemoveEditableTextbox, + OpenDocumentsSuppliedAtStartup, + // Send prefix: Send global, static data to the frontend that is never updated SendUIMetadata { #[serde(rename = "nodeDescriptions")] diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index cb852eb3bc..251acc1286 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::OpenDocumentsSuppliedAtStartup); } PortfolioMessage::DocumentPassMessage { document_id, message } => { if let Some(document) = self.documents.get_mut(&document_id) { From 05fabc751cd560252698ee751aef8db6835b2822 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 13 Oct 2025 20:16:05 +0000 Subject: [PATCH 10/12] Make ready for merge --- desktop/src/app.rs | 40 ++++++++++++++++-------------- desktop/src/cef/context/builder.rs | 19 +++++++++----- desktop/src/cef/platform.rs | 7 +----- desktop/src/cli.rs | 13 ++++------ desktop/src/main.rs | 2 +- 5 files changed, 42 insertions(+), 39 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 49be15409c..634531d88a 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,7 +41,7 @@ pub(crate) struct App { web_communication_initialized: bool, web_communication_startup_buffer: Vec>, persistent_data: PersistentData, - cli_files: Option>, + launch_documents: Vec, } impl App { @@ -49,7 +51,7 @@ impl App { wgpu_context: WgpuContext, app_event_receiver: Receiver, app_event_scheduler: AppEventScheduler, - cli_files: Option>, + 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); @@ -81,7 +83,7 @@ impl App { web_communication_startup_buffer: Vec::new(), persistent_data, native_window: Default::default(), - cli_files, + launch_documents, } } @@ -95,20 +97,22 @@ impl App { self.send_or_queue_web_message(bytes); } DesktopFrontendMessage::OpenDocumentsSuppliedAtStartup => { + if self.launch_documents.is_empty() { + return; + } let app_event_scheduler = self.app_event_scheduler.clone(); - if let Some(paths) = self.cli_files.clone() { - let _ = thread::spawn(move || { - for path in paths { - tracing::info!("Opening file from command line: {}", path.display()); - if let Ok(content) = std::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()); - } + 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()); } - }); - } + } + }); } DesktopFrontendMessage::OpenFileDialog { title, filters, context } => { let app_event_scheduler = self.app_event_scheduler.clone(); @@ -121,7 +125,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)); @@ -154,7 +158,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); } } @@ -408,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 833ecde6bd..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, disable_acceleration: bool) -> 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, disable_acceleration) + create_browser(event_handler, instance_dir, disable_gpu_acceleration) } #[cfg(not(target_os = "macos"))] - pub(crate) fn initialize(self, event_handler: H, disable_acceleration: bool) -> 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, disable_acceleration) { + 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, disable_acceleration: bool) -> 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(disable_acceleration) { 1 } else { 0 }, + shared_texture_enabled: use_accelerated_paint as i32, ..Default::default() }; diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index 763b9952eb..6bfa72de74 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,10 +1,5 @@ #[cfg(feature = "accelerated_paint")] -pub fn should_enable_hardware_acceleration(user_disabled: bool) -> bool { - if user_disabled { - // User explicitly disabled - return false; - } - +pub fn should_enable_hardware_acceleration() -> bool { // Auto-detect #[cfg(target_os = "linux")] { diff --git a/desktop/src/cli.rs b/desktop/src/cli.rs index 5e1479d60e..4e54bd2477 100644 --- a/desktop/src/cli.rs +++ b/desktop/src/cli.rs @@ -1,12 +1,9 @@ -use std::path::PathBuf; - -use clap::Parser; -#[derive(Debug, Parser)] -#[clap(name = "graphite-cli", version)] +#[derive(clap::Parser)] +#[clap(name = "graphite-editor", version)] pub struct Cli { #[arg(help = "Files to open on startup")] - pub files: Option>, + pub files: Vec, - #[arg(long, action = clap::ArgAction::SetTrue, help = "Disable UI accelerated painting using GPU")] - pub disable_ui_acceleration: Option, + #[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 997f7360b4..c0c5b974a9 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -46,7 +46,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, cli.disable_ui_acceleration.unwrap_or(false)) { + let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) { Ok(c) => { tracing::info!("CEF initialized successfully"); c From cf66d6da29db4f5811917ac420d9ea5338be6a58 Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 13 Oct 2025 20:29:12 +0000 Subject: [PATCH 11/12] Improve names --- desktop/src/app.rs | 50 +++++++++---------- desktop/src/main.rs | 3 +- .../wrapper/src/intercept_frontend_message.rs | 6 +-- desktop/wrapper/src/messages.rs | 2 +- .../src/messages/frontend/frontend_message.rs | 3 +- .../portfolio/portfolio_message_handler.rs | 2 +- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 634531d88a..0e2d9d86ca 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -96,24 +96,6 @@ impl App { }; self.send_or_queue_web_message(bytes); } - DesktopFrontendMessage::OpenDocumentsSuppliedAtStartup => { - 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()); - } - } - }); - } DesktopFrontendMessage::OpenFileDialog { title, filters, context } => { let app_event_scheduler = self.app_event_scheduler.clone(); let _ = thread::spawn(move || { @@ -220,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 { @@ -255,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()); + } + } + }); } } } diff --git a/desktop/src/main.rs b/desktop/src/main.rs index c0c5b974a9..430db35648 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -18,10 +18,9 @@ mod gpu_context; use app::App; use cef::CefHandler; +use cli::Cli; use event::CreateAppEventSchedulerEventLoopExt; -use crate::cli::Cli; - fn main() { tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).init(); diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index 5197ebf2b4..2b1cdb86fa 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -110,15 +110,15 @@ 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 }); } FrontendMessage::TriggerLoadPreferences => { dispatcher.respond(DesktopFrontendMessage::PersistenceLoadPreferences); } - FrontendMessage::OpenDocumentsSuppliedAtStartup => { - dispatcher.respond(DesktopFrontendMessage::OpenDocumentsSuppliedAtStartup); - } m => return Some(m), } None diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index 1edf798cb2..33704c2548 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -8,7 +8,7 @@ pub use graphite_editor::messages::prelude::PreferencesMessageHandler as Prefere pub enum DesktopFrontendMessage { ToWeb(Vec), - OpenDocumentsSuppliedAtStartup, + 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 480443bc7d..637b12425f 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -52,8 +52,6 @@ pub enum FrontendMessage { }, DisplayRemoveEditableTextbox, - OpenDocumentsSuppliedAtStartup, - // Send prefix: Send global, static data to the frontend that is never updated SendUIMetadata { #[serde(rename = "nodeDescriptions")] @@ -104,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 251acc1286..959a5df89a 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -154,7 +154,7 @@ impl MessageHandler> for Portfolio // Tell frontend to finish loading persistent documents responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments); - responses.add(FrontendMessage::OpenDocumentsSuppliedAtStartup); + responses.add(FrontendMessage::TriggerOpenLaunchDocuments); } PortfolioMessage::DocumentPassMessage { document_id, message } => { if let Some(document) = self.documents.get_mut(&document_id) { From bb650cac70b8d0105eab34abaeaf23e5a0d129ff Mon Sep 17 00:00:00 2001 From: Timon Schelling Date: Mon, 13 Oct 2025 20:34:02 +0000 Subject: [PATCH 12/12] remove comment --- desktop/src/cef/platform.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/src/cef/platform.rs b/desktop/src/cef/platform.rs index 6bfa72de74..7df5d5f982 100644 --- a/desktop/src/cef/platform.rs +++ b/desktop/src/cef/platform.rs @@ -1,6 +1,5 @@ #[cfg(feature = "accelerated_paint")] pub fn should_enable_hardware_acceleration() -> bool { - // Auto-detect #[cfg(target_os = "linux")] { // Check if running on Wayland or X11