Skip to content

Commit 5da75ec

Browse files
Desktop: Restart without UI Acceleration when it fails on Linux
1 parent b4e9d7b commit 5da75ec

File tree

3 files changed

+70
-27
lines changed

3 files changed

+70
-27
lines changed

desktop/src/app.rs

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use rand::Rng;
22
use rfd::AsyncFileDialog;
33
use std::fs;
4-
use std::path::PathBuf;
54
use std::sync::mpsc::{Receiver, Sender, SyncSender};
65
use std::thread;
76
use std::time::{Duration, Instant};
87
use winit::application::ApplicationHandler;
98
use winit::dpi::{PhysicalPosition, PhysicalSize};
109
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
11-
use winit::event_loop::{ActiveEventLoop, ControlFlow};
10+
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
1211
use winit::window::WindowId;
1312

1413
use crate::cef;
14+
use crate::cli::Cli;
1515
use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS;
1616
use crate::event::{AppEvent, AppEventScheduler};
1717
use crate::persist::PersistentData;
@@ -37,13 +37,14 @@ pub(crate) struct App {
3737
cef_context: Box<dyn cef::CefContext>,
3838
cef_schedule: Option<Instant>,
3939
cef_view_info_sender: Sender<cef::ViewInfoUpdate>,
40-
last_ui_update: Instant,
41-
avg_frame_time: f32,
40+
cef_init_successful: bool,
4241
start_render_sender: SyncSender<()>,
4342
web_communication_initialized: bool,
4443
web_communication_startup_buffer: Vec<Vec<u8>>,
4544
persistent_data: PersistentData,
46-
launch_documents: Vec<PathBuf>,
45+
cli: Cli,
46+
startup_time: Option<Instant>,
47+
exit_reason: ExitReason,
4748
}
4849

4950
impl App {
@@ -57,12 +58,12 @@ impl App {
5758
wgpu_context: WgpuContext,
5859
app_event_receiver: Receiver<AppEvent>,
5960
app_event_scheduler: AppEventScheduler,
60-
launch_documents: Vec<PathBuf>,
61+
cli: Cli,
6162
) -> Self {
6263
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
6364
ctrlc::set_handler(move || {
6465
tracing::info!("Termination signal received, exiting...");
65-
ctrlc_app_event_scheduler.schedule(AppEvent::CloseWindow);
66+
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
6667
})
6768
.expect("Error setting Ctrl-C handler");
6869

@@ -95,19 +96,32 @@ impl App {
9596
app_event_receiver,
9697
app_event_scheduler,
9798
desktop_wrapper,
98-
last_ui_update: Instant::now(),
9999
cef_context,
100100
cef_schedule: Some(Instant::now()),
101101
cef_view_info_sender,
102-
avg_frame_time: 0.,
102+
cef_init_successful: false,
103103
start_render_sender,
104104
web_communication_initialized: false,
105105
web_communication_startup_buffer: Vec::new(),
106106
persistent_data,
107-
launch_documents,
107+
cli,
108+
exit_reason: ExitReason::Shutdown,
109+
startup_time: None,
108110
}
109111
}
110112

113+
pub(crate) fn run(mut self, event_loop: EventLoop) -> ExitReason {
114+
event_loop.run_app(&mut self).unwrap();
115+
self.exit_reason
116+
}
117+
118+
fn exit(&mut self, reason: Option<ExitReason>) {
119+
if let Some(reason) = reason {
120+
self.exit_reason = reason;
121+
}
122+
self.app_event_scheduler.schedule(AppEvent::Exit);
123+
}
124+
111125
fn resize(&mut self) {
112126
let Some(window) = &self.window else {
113127
tracing::error!("Resize failed due to missing window");
@@ -302,11 +316,11 @@ impl App {
302316
}
303317
}
304318
DesktopFrontendMessage::OpenLaunchDocuments => {
305-
if self.launch_documents.is_empty() {
319+
if self.cli.files.is_empty() {
306320
return;
307321
}
308322
let app_event_scheduler = self.app_event_scheduler.clone();
309-
let launch_documents = std::mem::take(&mut self.launch_documents);
323+
let launch_documents = std::mem::take(&mut self.cli.files);
310324
let _ = thread::spawn(move || {
311325
for path in launch_documents {
312326
tracing::info!("Opening file from command line: {}", path.display());
@@ -343,7 +357,7 @@ impl App {
343357
}
344358
}
345359
DesktopFrontendMessage::WindowClose => {
346-
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
360+
self.app_event_scheduler.schedule(AppEvent::Exit);
347361
}
348362
DesktopFrontendMessage::WindowMinimize => {
349363
if let Some(window) = &self.window {
@@ -431,15 +445,13 @@ impl App {
431445
AppEvent::UiUpdate(texture) => {
432446
if let Some(render_state) = self.render_state.as_mut() {
433447
render_state.bind_ui_texture(texture);
434-
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
435-
self.last_ui_update = Instant::now();
436-
if elapsed < 0.5 {
437-
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
438-
}
439448
}
440449
if let Some(window) = &self.window {
441450
window.request_redraw();
442451
}
452+
if !self.cef_init_successful {
453+
self.cef_init_successful = true;
454+
}
443455
}
444456
AppEvent::ScheduleBrowserWork(instant) => {
445457
if instant <= Instant::now() {
@@ -453,9 +465,7 @@ impl App {
453465
window.set_cursor(event_loop, cursor);
454466
}
455467
}
456-
AppEvent::CloseWindow => {
457-
// TODO: Implement graceful shutdown
458-
468+
AppEvent::Exit => {
459469
tracing::info!("Exiting main event loop");
460470
event_loop.exit();
461471
}
@@ -481,6 +491,8 @@ impl ApplicationHandler for App {
481491
self.resize();
482492

483493
self.desktop_wrapper.init(self.wgpu_context.clone());
494+
495+
self.startup_time = Some(Instant::now());
484496
}
485497

486498
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
@@ -489,7 +501,7 @@ impl ApplicationHandler for App {
489501
}
490502
}
491503

492-
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
504+
fn window_event(&mut self, _event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
493505
// Handle pointer lock release
494506
if let Some(pointer_lock_position) = self.pointer_lock_position
495507
&& let WindowEvent::PointerButton {
@@ -514,7 +526,7 @@ impl ApplicationHandler for App {
514526

515527
match event {
516528
WindowEvent::CloseRequested => {
517-
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
529+
self.app_event_scheduler.schedule(AppEvent::Exit);
518530
}
519531
WindowEvent::SurfaceResized(_) | WindowEvent::ScaleFactorChanged { .. } => {
520532
self.resize();
@@ -539,12 +551,22 @@ impl ApplicationHandler for App {
539551
}
540552
Err(RenderError::SurfaceError(wgpu::SurfaceError::OutOfMemory)) => {
541553
tracing::error!("GPU out of memory");
542-
event_loop.exit();
554+
self.exit(None);
543555
}
544556
Err(RenderError::SurfaceError(e)) => tracing::error!("Render error: {:?}", e),
545557
}
546558
let _ = self.start_render_sender.try_send(());
547559
}
560+
561+
if !self.cef_init_successful
562+
&& !self.cli.disable_ui_acceleration
563+
&& self.web_communication_initialized
564+
&& let Some(startup_time) = self.startup_time
565+
&& startup_time.elapsed() > Duration::from_secs(3)
566+
{
567+
tracing::error!("UI acceleration not working, exiting.");
568+
self.exit(Some(ExitReason::UiAccalerationFailure));
569+
}
548570
}
549571
WindowEvent::DragDropped { paths, .. } => {
550572
for path in paths {
@@ -629,3 +651,8 @@ impl ApplicationHandler for App {
629651
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
630652
}
631653
}
654+
655+
pub(crate) enum ExitReason {
656+
Shutdown,
657+
UiAccalerationFailure,
658+
}

desktop/src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub(crate) enum AppEvent {
88
WebCommunicationInitialized,
99
DesktopWrapperMessage(DesktopWrapperMessage),
1010
NodeGraphExecutionResult(NodeGraphExecutionResult),
11-
CloseWindow,
11+
Exit,
1212
#[cfg(target_os = "macos")]
1313
MenuEvent {
1414
id: String,

desktop/src/lib.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub fn start() {
7777

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

80+
if cli.disable_ui_acceleration {
81+
println!("UI acceleration is disabled");
82+
}
83+
8084
let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver);
8185
let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) {
8286
Ok(context) => {
@@ -101,13 +105,25 @@ pub fn start() {
101105
}
102106
};
103107

104-
let mut app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files);
108+
let app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli);
105109

106-
event_loop.run_app(&mut app).unwrap();
110+
let exit_reason = app.run(event_loop);
107111

108112
// Explicitly drop the instance lock
109113
drop(lock);
110114

115+
match exit_reason {
116+
#[cfg(target_os = "linux")]
117+
app::ExitReason::UiAccalerationFailure => {
118+
use std::os::unix::process::CommandExt;
119+
120+
tracing::error!("Restarting application without UI acceleration");
121+
let _ = std::process::Command::new(std::env::current_exe().unwrap()).arg("--disable-ui-acceleration").exec();
122+
tracing::error!("Failed to restart application");
123+
}
124+
_ => {}
125+
}
126+
111127
// Workaround for a Windows-specific exception that occurs when `app` is dropped.
112128
// The issue causes the window to hang for a few seconds before closing.
113129
// Appears to be related to CEF object destruction order.

0 commit comments

Comments
 (0)