11use rand:: Rng ;
22use rfd:: AsyncFileDialog ;
33use std:: fs;
4- use std:: path:: PathBuf ;
54use std:: sync:: mpsc:: { Receiver , Sender , SyncSender } ;
65use std:: thread;
76use std:: time:: { Duration , Instant } ;
87use winit:: application:: ApplicationHandler ;
98use winit:: dpi:: { PhysicalPosition , PhysicalSize } ;
109use winit:: event:: { ButtonSource , ElementState , MouseButton , WindowEvent } ;
11- use winit:: event_loop:: { ActiveEventLoop , ControlFlow } ;
10+ use winit:: event_loop:: { ActiveEventLoop , ControlFlow , EventLoop } ;
1211use winit:: window:: WindowId ;
1312
1413use crate :: cef;
14+ use crate :: cli:: Cli ;
1515use crate :: consts:: CEF_MESSAGE_LOOP_MAX_ITERATIONS ;
1616use crate :: event:: { AppEvent , AppEventScheduler } ;
1717use 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
4950impl 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+ }
0 commit comments