Skip to content
Merged
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
75 changes: 51 additions & 24 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use rand::Rng;
use rfd::AsyncFileDialog;
use std::fs;
use std::path::PathBuf;
use std::sync::mpsc::{Receiver, Sender, SyncSender};
use std::thread;
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::WindowId;

use crate::cef;
use crate::cli::Cli;
use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS;
use crate::event::{AppEvent, AppEventScheduler};
use crate::persist::PersistentData;
Expand All @@ -37,13 +37,14 @@ pub(crate) struct App {
cef_context: Box<dyn cef::CefContext>,
cef_schedule: Option<Instant>,
cef_view_info_sender: Sender<cef::ViewInfoUpdate>,
last_ui_update: Instant,
avg_frame_time: f32,
cef_init_successful: bool,
start_render_sender: SyncSender<()>,
web_communication_initialized: bool,
web_communication_startup_buffer: Vec<Vec<u8>>,
persistent_data: PersistentData,
launch_documents: Vec<PathBuf>,
cli: Cli,
startup_time: Option<Instant>,
exit_reason: ExitReason,
}

impl App {
Expand All @@ -57,12 +58,12 @@ impl App {
wgpu_context: WgpuContext,
app_event_receiver: Receiver<AppEvent>,
app_event_scheduler: AppEventScheduler,
launch_documents: Vec<PathBuf>,
cli: Cli,
) -> Self {
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
ctrlc::set_handler(move || {
tracing::info!("Termination signal received, exiting...");
ctrlc_app_event_scheduler.schedule(AppEvent::CloseWindow);
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
})
.expect("Error setting Ctrl-C handler");

Expand Down Expand Up @@ -95,19 +96,32 @@ impl App {
app_event_receiver,
app_event_scheduler,
desktop_wrapper,
last_ui_update: Instant::now(),
cef_context,
cef_schedule: Some(Instant::now()),
cef_view_info_sender,
avg_frame_time: 0.,
cef_init_successful: false,
start_render_sender,
web_communication_initialized: false,
web_communication_startup_buffer: Vec::new(),
persistent_data,
launch_documents,
cli,
exit_reason: ExitReason::Shutdown,
startup_time: None,
}
}

pub(crate) fn run(mut self, event_loop: EventLoop) -> ExitReason {
event_loop.run_app(&mut self).unwrap();
self.exit_reason
}

fn exit(&mut self, reason: Option<ExitReason>) {
if let Some(reason) = reason {
self.exit_reason = reason;
}
self.app_event_scheduler.schedule(AppEvent::Exit);
}

fn resize(&mut self) {
let Some(window) = &self.window else {
tracing::error!("Resize failed due to missing window");
Expand Down Expand Up @@ -302,11 +316,11 @@ impl App {
}
}
DesktopFrontendMessage::OpenLaunchDocuments => {
if self.launch_documents.is_empty() {
if self.cli.files.is_empty() {
return;
}
let app_event_scheduler = self.app_event_scheduler.clone();
let launch_documents = std::mem::take(&mut self.launch_documents);
let launch_documents = std::mem::take(&mut self.cli.files);
let _ = thread::spawn(move || {
for path in launch_documents {
tracing::info!("Opening file from command line: {}", path.display());
Expand Down Expand Up @@ -343,7 +357,7 @@ impl App {
}
}
DesktopFrontendMessage::WindowClose => {
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
self.app_event_scheduler.schedule(AppEvent::Exit);
}
DesktopFrontendMessage::WindowMinimize => {
if let Some(window) = &self.window {
Expand Down Expand Up @@ -431,15 +445,13 @@ impl App {
AppEvent::UiUpdate(texture) => {
if let Some(render_state) = self.render_state.as_mut() {
render_state.bind_ui_texture(texture);
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
self.last_ui_update = Instant::now();
if elapsed < 0.5 {
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
}
}
if let Some(window) = &self.window {
window.request_redraw();
}
if !self.cef_init_successful {
self.cef_init_successful = true;
}
}
AppEvent::ScheduleBrowserWork(instant) => {
if instant <= Instant::now() {
Expand All @@ -453,9 +465,7 @@ impl App {
window.set_cursor(event_loop, cursor);
}
}
AppEvent::CloseWindow => {
// TODO: Implement graceful shutdown

AppEvent::Exit => {
tracing::info!("Exiting main event loop");
event_loop.exit();
}
Expand All @@ -481,6 +491,8 @@ impl ApplicationHandler for App {
self.resize();

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

self.startup_time = Some(Instant::now());
}

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

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

match event {
WindowEvent::CloseRequested => {
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
self.app_event_scheduler.schedule(AppEvent::Exit);
}
WindowEvent::SurfaceResized(_) | WindowEvent::ScaleFactorChanged { .. } => {
self.resize();
Expand All @@ -539,12 +551,22 @@ impl ApplicationHandler for App {
}
Err(RenderError::SurfaceError(wgpu::SurfaceError::OutOfMemory)) => {
tracing::error!("GPU out of memory");
event_loop.exit();
self.exit(None);
}
Err(RenderError::SurfaceError(e)) => tracing::error!("Render error: {:?}", e),
}
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 @@ -629,3 +651,8 @@ impl ApplicationHandler for App {
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
}
}

pub(crate) enum ExitReason {
Shutdown,
UiAccelerationFailure,
}
2 changes: 1 addition & 1 deletion desktop/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) enum AppEvent {
WebCommunicationInitialized,
DesktopWrapperMessage(DesktopWrapperMessage),
NodeGraphExecutionResult(NodeGraphExecutionResult),
CloseWindow,
Exit,
#[cfg(target_os = "macos")]
MenuEvent {
id: String,
Expand Down
20 changes: 18 additions & 2 deletions desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ pub fn start() {

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

if cli.disable_ui_acceleration {
println!("UI acceleration is disabled");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be an eprintln ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more of an info that an error, so in my opinion no.
but this will only stay for a couple of weeks anyways (until rc4).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is more if it should be included if I pipe the output of graphite to a file, and I'd think it should probably not. E.g. the dbg! macro also outputs to stderr, and I would argue that is similar in nature

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we should also change the Available GPU lines?
I would merge this and we can decide later for all similar cases, currently most stuff ends up in stdout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we should, from man stderr

Under normal circumstances every UNIX program has three streams opened for it when
it starts up, one for input, one for output, and one for printing diagnostic or
error messages.

}

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) {
Ok(context) => {
Expand All @@ -101,13 +105,25 @@ pub fn start() {
}
};

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

event_loop.run_app(&mut app).unwrap();
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");
}
_ => {}
}

// Workaround for a Windows-specific exception that occurs when `app` is dropped.
// The issue causes the window to hang for a few seconds before closing.
// Appears to be related to CEF object destruction order.
Expand Down