Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(crate) struct App {
web_communication_startup_buffer: Vec<Vec<u8>>,
persistent_data: PersistentData,
cli: Cli,
startup_time: Option<Instant>,
compatibility_mode: bool,
exit_reason: ExitReason,
}

Expand All @@ -59,6 +59,7 @@ impl App {
app_event_receiver: Receiver<AppEvent>,
app_event_scheduler: AppEventScheduler,
cli: Cli,
compatibility_mode: bool,
) -> Self {
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
ctrlc::set_handler(move || {
Expand All @@ -81,7 +82,7 @@ impl App {
let mut persistent_data = PersistentData::default();
persistent_data.load_from_disk();

let desktop_wrapper = DesktopWrapper::new(rand::rng().random());
let desktop_wrapper = DesktopWrapper::new(rand::rng().random(), compatibility_mode);

Self {
render_state: None,
Expand All @@ -106,8 +107,8 @@ impl App {
web_communication_startup_buffer: Vec::new(),
persistent_data,
cli,
compatibility_mode,
exit_reason: ExitReason::Shutdown,
startup_time: None,
}
}

Expand Down Expand Up @@ -398,6 +399,9 @@ impl App {
window.show_all();
}
}
DesktopFrontendMessage::RelaunchWithUiAcceleration => {
self.exit(Some(ExitReason::RelaunchWithUiAcceleration));
}
}
}

Expand Down Expand Up @@ -457,6 +461,12 @@ impl App {
self.cef_init_successful = true;
}
}
AppEvent::StartupTextureWatchdogTimeout => {
if !self.cef_init_successful && !self.compatibility_mode {
tracing::error!("No CEF texture received within the first second, restarting in compatibility mode");
self.exit(Some(ExitReason::UiAccelerationFailure));
}
}
AppEvent::ScheduleBrowserWork(instant) => {
if instant <= Instant::now() {
self.cef_context.work();
Expand Down Expand Up @@ -496,7 +506,13 @@ impl ApplicationHandler for App {

self.desktop_wrapper.init(self.wgpu_context.clone());

self.startup_time = Some(Instant::now());
if !self.compatibility_mode {
let app_event_scheduler = self.app_event_scheduler.clone();
let _ = thread::spawn(move || {
thread::sleep(Duration::from_secs(1));
app_event_scheduler.schedule(AppEvent::StartupTextureWatchdogTimeout);
});
}
}

fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
Expand Down Expand Up @@ -561,16 +577,6 @@ impl ApplicationHandler for App {
}
let _ = self.start_render_sender.try_send(());
}

if !self.cef_init_successful
&& !self.cli.disable_ui_acceleration
&& self.web_communication_initialized
&& let Some(startup_time) = self.startup_time
&& startup_time.elapsed() > Duration::from_secs(3)
{
tracing::error!("UI acceleration not working, exiting.");
self.exit(Some(ExitReason::UiAccelerationFailure));
}
}
WindowEvent::DragDropped { paths, .. } => {
for path in paths {
Expand Down Expand Up @@ -664,4 +670,5 @@ impl ApplicationHandler for App {
pub(crate) enum ExitReason {
Shutdown,
UiAccelerationFailure,
RelaunchWithUiAcceleration,
}
1 change: 1 addition & 0 deletions desktop/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub(crate) const APP_DIRECTORY_NAME: &str = "Graphite";
pub(crate) const APP_LOCK_FILE_NAME: &str = "instance.lock";
pub(crate) const APP_STATE_FILE_NAME: &str = "state.ron";
pub(crate) const APP_PREFERENCES_FILE_NAME: &str = "preferences.ron";
pub(crate) const APP_STARTUP_SETTINGS_FILE_NAME: &str = "startup-settings.ron";
pub(crate) const APP_DOCUMENTS_DIRECTORY_NAME: &str = "documents";

// CEF configuration constants
Expand Down
1 change: 1 addition & 0 deletions desktop/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::wrapper::messages::DesktopWrapperMessage;

pub(crate) enum AppEvent {
UiUpdate(wgpu::Texture),
StartupTextureWatchdogTimeout,
CursorChange(crate::window::Cursor),
ScheduleBrowserWork(std::time::Instant),
WebCommunicationInitialized,
Expand Down
52 changes: 43 additions & 9 deletions desktop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use clap::Parser;
use std::ffi::OsStr;
use std::io::Write;
use std::process::exit;
use tracing_subscriber::EnvFilter;
Expand Down Expand Up @@ -40,6 +41,8 @@ pub fn start() {
}

let cli = Cli::parse();
let mut startup_settings = persist::StartupSettings::load_from_disk();
let compatibility_mode = cli.disable_ui_acceleration || startup_settings.ui_compatibility_mode;

let Ok(lock_file) = std::fs::OpenOptions::new()
.read(true)
Expand Down Expand Up @@ -77,12 +80,12 @@ pub fn start() {

let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel();

if cli.disable_ui_acceleration {
if compatibility_mode {
println!("UI acceleration is disabled");
}

let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver);
let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) {
let cef_context = match cef_context_builder.initialize(cef_handler, compatibility_mode) {
Ok(context) => {
tracing::info!("CEF initialized successfully");
context
Expand All @@ -105,23 +108,39 @@ pub fn start() {
}
};

let app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli);
let app = App::new(
Box::new(cef_context),
cef_view_info_sender,
wgpu_context,
app_event_receiver,
app_event_scheduler,
cli,
compatibility_mode,
);

let exit_reason = app.run(event_loop);

// Explicitly drop the instance lock
drop(lock);

match exit_reason {
#[cfg(target_os = "linux")]
app::ExitReason::UiAccelerationFailure => {
use std::os::unix::process::CommandExt;

tracing::error!("Restarting application without UI acceleration");
let _ = std::process::Command::new(std::env::current_exe().unwrap()).arg("--disable-ui-acceleration").exec();
tracing::error!("Failed to restart application");
startup_settings.ui_compatibility_mode = true;
startup_settings.save_to_disk();
if let Err(error) = restart_application(true) {
tracing::error!("Failed to restart application: {error}");
}
}
app::ExitReason::RelaunchWithUiAcceleration => {
tracing::info!("Restarting application with UI acceleration");
startup_settings.ui_compatibility_mode = false;
startup_settings.save_to_disk();
if let Err(error) = restart_application(false) {
tracing::error!("Failed to restart application: {error}");
}
}
_ => {}
app::ExitReason::Shutdown => {}
}

// Workaround for a Windows-specific exception that occurs when `app` is dropped.
Expand All @@ -133,6 +152,21 @@ pub fn start() {
exit(0);
}

fn restart_application(disable_ui_acceleration: bool) -> std::io::Result<()> {
let current_exe = std::env::current_exe()?;
let args = restart_arguments(disable_ui_acceleration);
std::process::Command::new(current_exe).args(args).spawn().map(|_| ())
}

fn restart_arguments(disable_ui_acceleration: bool) -> Vec<std::ffi::OsString> {
let disable_flag = OsStr::new("--disable-ui-acceleration");
let mut args = std::env::args_os().skip(1).filter(|arg| arg != disable_flag).collect::<Vec<_>>();
if disable_ui_acceleration {
args.push(disable_flag.into());
}
args
}

pub fn start_helper() {
let cef_context_builder = cef::CefContextBuilder::<CefHandler>::new_helper();
assert!(cef_context_builder.is_sub_process());
Expand Down
47 changes: 47 additions & 0 deletions desktop/src/persist.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
use crate::wrapper::messages::{Document, DocumentId, Preferences};

#[derive(Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct StartupSettings {
#[serde(default)]
pub(crate) ui_compatibility_mode: bool,
}

impl StartupSettings {
pub(crate) fn load_from_disk() -> Self {
let path = Self::file_path();
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Self::default(),
Err(e) => {
tracing::error!("Failed to read startup settings from disk: {e}");
return Self::default();
}
};

match ron::from_str(&data) {
Ok(settings) => settings,
Err(e) => {
tracing::error!("Failed to deserialize startup settings: {e}");
Self::default()
}
}
}

pub(crate) fn save_to_disk(&self) {
let data = match ron::ser::to_string_pretty(self, Default::default()) {
Ok(d) => d,
Err(e) => {
tracing::error!("Failed to serialize startup settings: {e}");
return;
}
};
if let Err(e) = std::fs::write(Self::file_path(), data) {
tracing::error!("Failed to write startup settings to disk: {e}");
}
}

fn file_path() -> std::path::PathBuf {
let mut path = crate::dirs::app_data_dir();
path.push(crate::consts::APP_STARTUP_SETTINGS_FILE_NAME);
path
}
}

#[derive(Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct PersistentData {
documents: DocumentStore,
Expand Down
3 changes: 3 additions & 0 deletions desktop/wrapper/src/intercept_frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
FrontendMessage::WindowShowAll => {
dispatcher.respond(DesktopFrontendMessage::WindowShowAll);
}
FrontendMessage::WindowRelaunchWithUiAcceleration => {
dispatcher.respond(DesktopFrontendMessage::RelaunchWithUiAcceleration);
}
m => return Some(m),
}
None
Expand Down
8 changes: 6 additions & 2 deletions desktop/wrapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@ pub struct DesktopWrapper {
}

impl DesktopWrapper {
pub fn new(uuid_random_seed: u64) -> Self {
pub fn new(uuid_random_seed: u64, compatibility_mode: bool) -> Self {
#[cfg(target_os = "windows")]
let host = Host::Windows;
#[cfg(target_os = "macos")]
let host = Host::Mac;
#[cfg(target_os = "linux")]
let host = Host::Linux;
let env = Environment { platform: Platform::Desktop, host };
let env = Environment {
platform: Platform::Desktop,
host,
compatibility_mode,
};

Self {
editor: Editor::new(env, uuid_random_seed),
Expand Down
1 change: 1 addition & 0 deletions desktop/wrapper/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub enum DesktopFrontendMessage {
WindowHide,
WindowHideOthers,
WindowShowAll,
RelaunchWithUiAcceleration,
}

pub enum DesktopWrapperMessage {
Expand Down
5 changes: 5 additions & 0 deletions editor/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl Editor {
&Environment {
platform: Platform::Desktop,
host: Host::Linux,
compatibility_mode: false,
}
}
}
Expand All @@ -59,6 +60,7 @@ impl Editor {
pub struct Environment {
pub platform: Platform,
pub host: Host,
pub compatibility_mode: bool,
}
#[derive(Clone, Copy, Debug)]
pub enum Platform {
Expand Down Expand Up @@ -87,6 +89,9 @@ impl Environment {
pub fn is_linux(&self) -> bool {
matches!(self.host, Host::Linux)
}
pub fn is_compatibility_mode(&self) -> bool {
self.compatibility_mode
}
}

pub const GRAPHITE_RELEASE_SERIES: &str = env!("GRAPHITE_RELEASE_SERIES");
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/app_window/app_window_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ pub enum AppWindowMessage {
Hide,
HideOthers,
ShowAll,
RelaunchWithUiAcceleration,
}
3 changes: 3 additions & 0 deletions editor/src/messages/app_window/app_window_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
AppWindowMessage::ShowAll => {
responses.add(FrontendMessage::WindowShowAll);
}
AppWindowMessage::RelaunchWithUiAcceleration => {
responses.add(FrontendMessage::WindowRelaunchWithUiAcceleration);
}
}
}
advertise_actions!(AppWindowMessageDiscriminant;
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/frontend/frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,5 @@ pub enum FrontendMessage {
WindowHide,
WindowHideOthers,
WindowShowAll,
WindowRelaunchWithUiAcceleration,
}
15 changes: 14 additions & 1 deletion editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,20 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
}
PortfolioMessage::RequestStatusBarInfoLayout => {
#[cfg(not(target_family = "wasm"))]
let widgets = vec![TextLabel::new("Graphite 1.0.0-RC3").disabled(true).widget_instance()]; // TODO: After the RCs, call this "Graphite (beta) x.y.z"
let widgets = {
let mut widgets = Vec::new();
if Editor::environment().is_compatibility_mode() {
widgets.push(
IconButton::new("Warning", 12)
.tooltip_label("Compatibility mode")
.tooltip_description("Running without hardware acceleration. Click to relaunch with acceleration enabled.")
.on_update(|_| AppWindowMessage::RelaunchWithUiAcceleration.into())
.widget_instance(),
);
}
widgets.push(TextLabel::new("Graphite 1.0.0-RC3").disabled(true).widget_instance()); // TODO: After the RCs, call this "Graphite (beta) x.y.z"
widgets
};
#[cfg(target_family = "wasm")]
let widgets = vec![];

Expand Down
1 change: 1 addition & 0 deletions frontend/wasm/src/editor_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ impl EditorHandle {
"Windows" => Host::Windows,
_ => unreachable!(),
},
compatibility_mode: false,
},
uuid_random_seed,
);
Expand Down
Loading