1
- use crate :: CustomEvent ;
2
- use crate :: cef:: WindowSize ;
3
- use crate :: consts:: { APP_NAME , CEF_MESSAGE_LOOP_MAX_ITERATIONS } ;
4
- use crate :: persist:: PersistentData ;
5
- use crate :: render:: GraphicsState ;
6
- use graphite_desktop_wrapper:: messages:: { DesktopFrontendMessage , DesktopWrapperMessage , Platform } ;
7
- use graphite_desktop_wrapper:: { DesktopWrapper , NodeGraphExecutionResult , WgpuContext , serialize_frontend_messages} ;
8
-
9
1
use rfd:: AsyncFileDialog ;
10
2
use std:: sync:: Arc ;
3
+ use std:: sync:: mpsc:: Receiver ;
11
4
use std:: sync:: mpsc:: Sender ;
12
5
use std:: sync:: mpsc:: SyncSender ;
13
6
use std:: thread;
14
7
use std:: time:: Duration ;
15
8
use std:: time:: Instant ;
16
9
use winit:: application:: ApplicationHandler ;
17
- use winit:: dpi:: PhysicalSize ;
18
10
use winit:: event:: WindowEvent ;
19
11
use winit:: event_loop:: ActiveEventLoop ;
20
12
use winit:: event_loop:: ControlFlow ;
21
- use winit:: event_loop:: EventLoopProxy ;
22
13
use winit:: window:: Window ;
23
14
use winit:: window:: WindowId ;
24
15
25
16
use crate :: cef;
17
+ use crate :: consts:: CEF_MESSAGE_LOOP_MAX_ITERATIONS ;
18
+ use crate :: event:: { AppEvent , AppEventScheduler } ;
26
19
use crate :: native_window;
20
+ use crate :: persist:: PersistentData ;
21
+ use crate :: render:: GraphicsState ;
22
+ use graphite_desktop_wrapper:: messages:: { DesktopFrontendMessage , DesktopWrapperMessage , Platform } ;
23
+ use graphite_desktop_wrapper:: { DesktopWrapper , NodeGraphExecutionResult , WgpuContext , serialize_frontend_messages} ;
27
24
28
- pub ( crate ) struct WinitApp {
25
+ pub ( crate ) struct App {
29
26
cef_context : Box < dyn cef:: CefContext > ,
30
- window : Option < Arc < Window > > ,
27
+ window : Option < Arc < dyn Window > > ,
31
28
native_window : native_window:: NativeWindowHandle ,
32
29
cef_schedule : Option < Instant > ,
33
- window_size_sender : Sender < WindowSize > ,
30
+ cef_window_size_sender : Sender < cef :: WindowSize > ,
34
31
graphics_state : Option < GraphicsState > ,
35
32
wgpu_context : WgpuContext ,
36
- event_loop_proxy : EventLoopProxy < CustomEvent > ,
33
+ app_event_receiver : Receiver < AppEvent > ,
34
+ app_event_scheduler : AppEventScheduler ,
37
35
desktop_wrapper : DesktopWrapper ,
38
36
last_ui_update : Instant ,
39
37
avg_frame_time : f32 ,
@@ -43,14 +41,20 @@ pub(crate) struct WinitApp {
43
41
persistent_data : PersistentData ,
44
42
}
45
43
46
- impl WinitApp {
47
- pub ( crate ) fn new ( cef_context : Box < dyn cef:: CefContext > , window_size_sender : Sender < WindowSize > , wgpu_context : WgpuContext , event_loop_proxy : EventLoopProxy < CustomEvent > ) -> Self {
48
- let rendering_loop_proxy = event_loop_proxy. clone ( ) ;
44
+ impl App {
45
+ pub ( crate ) fn new (
46
+ cef_context : Box < dyn cef:: CefContext > ,
47
+ window_size_sender : Sender < cef:: WindowSize > ,
48
+ wgpu_context : WgpuContext ,
49
+ app_event_receiver : Receiver < AppEvent > ,
50
+ app_event_scheduler : AppEventScheduler ,
51
+ ) -> Self {
52
+ let rendering_app_event_scheduler = app_event_scheduler. clone ( ) ;
49
53
let ( start_render_sender, start_render_receiver) = std:: sync:: mpsc:: sync_channel ( 1 ) ;
50
54
std:: thread:: spawn ( move || {
51
55
loop {
52
56
let result = futures:: executor:: block_on ( DesktopWrapper :: execute_node_graph ( ) ) ;
53
- let _ = rendering_loop_proxy . send_event ( CustomEvent :: NodeGraphExecutionResult ( result) ) ;
57
+ rendering_app_event_scheduler . schedule ( AppEvent :: NodeGraphExecutionResult ( result) ) ;
54
58
let _ = start_render_receiver. recv ( ) ;
55
59
}
56
60
} ) ;
@@ -63,9 +67,10 @@ impl WinitApp {
63
67
window : None ,
64
68
cef_schedule : Some ( Instant :: now ( ) ) ,
65
69
graphics_state : None ,
66
- window_size_sender,
70
+ cef_window_size_sender : window_size_sender,
67
71
wgpu_context,
68
- event_loop_proxy,
72
+ app_event_receiver,
73
+ app_event_scheduler,
69
74
desktop_wrapper : DesktopWrapper :: new ( ) ,
70
75
last_ui_update : Instant :: now ( ) ,
71
76
avg_frame_time : 0. ,
@@ -87,7 +92,7 @@ impl WinitApp {
87
92
self . send_or_queue_web_message ( bytes) ;
88
93
}
89
94
DesktopFrontendMessage :: OpenFileDialog { title, filters, context } => {
90
- let event_loop_proxy = self . event_loop_proxy . clone ( ) ;
95
+ let app_event_scheduler = self . app_event_scheduler . clone ( ) ;
91
96
let _ = thread:: spawn ( move || {
92
97
let mut dialog = AsyncFileDialog :: new ( ) . set_title ( title) ;
93
98
for filter in filters {
@@ -100,7 +105,7 @@ impl WinitApp {
100
105
&& let Ok ( content) = std:: fs:: read ( & path)
101
106
{
102
107
let message = DesktopWrapperMessage :: OpenFileDialogResult { path, content, context } ;
103
- let _ = event_loop_proxy . send_event ( CustomEvent :: DesktopWrapperMessage ( message) ) ;
108
+ app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
104
109
}
105
110
} ) ;
106
111
}
@@ -111,7 +116,7 @@ impl WinitApp {
111
116
filters,
112
117
context,
113
118
} => {
114
- let event_loop_proxy = self . event_loop_proxy . clone ( ) ;
119
+ let app_event_scheduler = self . app_event_scheduler . clone ( ) ;
115
120
let _ = thread:: spawn ( move || {
116
121
let mut dialog = AsyncFileDialog :: new ( ) . set_title ( title) . set_file_name ( default_filename) ;
117
122
if let Some ( folder) = default_folder {
@@ -125,7 +130,7 @@ impl WinitApp {
125
130
126
131
if let Some ( path) = futures:: executor:: block_on ( show_dialog) {
127
132
let message = DesktopWrapperMessage :: SaveFileDialogResult { path, context } ;
128
- let _ = event_loop_proxy . send_event ( CustomEvent :: DesktopWrapperMessage ( message) ) ;
133
+ app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
129
134
}
130
135
} ) ;
131
136
}
@@ -145,7 +150,7 @@ impl WinitApp {
145
150
if let Some ( graphics_state) = & mut self . graphics_state
146
151
&& let Some ( window) = & self . window
147
152
{
148
- let window_size = window. inner_size ( ) ;
153
+ let window_size = window. surface_size ( ) ;
149
154
150
155
let viewport_offset_x = x / window_size. width as f32 ;
151
156
let viewport_offset_y = y / window_size. height as f32 ;
@@ -173,7 +178,7 @@ impl WinitApp {
173
178
}
174
179
}
175
180
DesktopFrontendMessage :: CloseWindow => {
176
- let _ = self . event_loop_proxy . send_event ( CustomEvent :: CloseWindow ) ;
181
+ self . app_event_scheduler . schedule ( AppEvent :: CloseWindow ) ;
177
182
}
178
183
DesktopFrontendMessage :: PersistenceWriteDocument { id, document } => {
179
184
self . persistent_data . write_document ( id, document) ;
@@ -252,71 +257,17 @@ impl WinitApp {
252
257
self . web_communication_startup_buffer . push ( message) ;
253
258
}
254
259
}
255
- }
256
-
257
- impl ApplicationHandler < CustomEvent > for WinitApp {
258
- fn about_to_wait ( & mut self , event_loop : & ActiveEventLoop ) {
259
- // Set a timeout in case we miss any cef schedule requests
260
- let timeout = Instant :: now ( ) + Duration :: from_millis ( 10 ) ;
261
- let wait_until = timeout. min ( self . cef_schedule . unwrap_or ( timeout) ) ;
262
- if let Some ( schedule) = self . cef_schedule
263
- && schedule < Instant :: now ( )
264
- {
265
- self . cef_schedule = None ;
266
- // Poll cef message loop multiple times to avoid message loop starvation
267
- for _ in 0 ..CEF_MESSAGE_LOOP_MAX_ITERATIONS {
268
- self . cef_context . work ( ) ;
269
- }
270
- }
271
- if let Some ( window) = & self . window . as_ref ( ) {
272
- window. request_redraw ( ) ;
273
- }
274
-
275
- event_loop. set_control_flow ( ControlFlow :: WaitUntil ( wait_until) ) ;
276
- }
277
-
278
- fn resumed ( & mut self , event_loop : & ActiveEventLoop ) {
279
- let mut window = Window :: default_attributes ( )
280
- . with_title ( APP_NAME )
281
- . with_min_inner_size ( winit:: dpi:: LogicalSize :: new ( 400 , 300 ) )
282
- . with_inner_size ( winit:: dpi:: LogicalSize :: new ( 1200 , 800 ) )
283
- . with_resizable ( true ) ;
284
-
285
- window = self . native_window . build ( window, event_loop) ;
286
-
287
- let window = event_loop. create_window ( window) . unwrap ( ) ;
288
-
289
- self . native_window . setup ( & window) ;
290
-
291
- let window = Arc :: new ( window) ;
292
- let graphics_state = GraphicsState :: new ( window. clone ( ) , self . wgpu_context . clone ( ) ) ;
293
-
294
- self . window = Some ( window) ;
295
- self . graphics_state = Some ( graphics_state) ;
296
-
297
- tracing:: info!( "Winit window created and ready" ) ;
298
-
299
- self . desktop_wrapper . init ( self . wgpu_context . clone ( ) ) ;
300
-
301
- #[ cfg( target_os = "windows" ) ]
302
- let platform = Platform :: Windows ;
303
- #[ cfg( target_os = "macos" ) ]
304
- let platform = Platform :: Mac ;
305
- #[ cfg( target_os = "linux" ) ]
306
- let platform = Platform :: Linux ;
307
- self . dispatch_desktop_wrapper_message ( DesktopWrapperMessage :: UpdatePlatform ( platform) ) ;
308
- }
309
260
310
- fn user_event ( & mut self , event_loop : & ActiveEventLoop , event : CustomEvent ) {
261
+ fn user_event ( & mut self , event_loop : & dyn ActiveEventLoop , event : AppEvent ) {
311
262
match event {
312
- CustomEvent :: WebCommunicationInitialized => {
263
+ AppEvent :: WebCommunicationInitialized => {
313
264
self . web_communication_initialized = true ;
314
265
for message in self . web_communication_startup_buffer . drain ( ..) {
315
266
self . cef_context . send_web_message ( message) ;
316
267
}
317
268
}
318
- CustomEvent :: DesktopWrapperMessage ( message) => self . dispatch_desktop_wrapper_message ( message) ,
319
- CustomEvent :: NodeGraphExecutionResult ( result) => match result {
269
+ AppEvent :: DesktopWrapperMessage ( message) => self . dispatch_desktop_wrapper_message ( message) ,
270
+ AppEvent :: NodeGraphExecutionResult ( result) => match result {
320
271
NodeGraphExecutionResult :: HasRun ( texture) => {
321
272
self . dispatch_desktop_wrapper_message ( DesktopWrapperMessage :: PollNodeGraphEvaluation ) ;
322
273
if let Some ( texture) = texture
@@ -329,7 +280,7 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
329
280
}
330
281
NodeGraphExecutionResult :: NotRun => { }
331
282
} ,
332
- CustomEvent :: UiUpdate ( texture) => {
283
+ AppEvent :: UiUpdate ( texture) => {
333
284
if let Some ( graphics_state) = self . graphics_state . as_mut ( ) {
334
285
graphics_state. resize ( texture. width ( ) , texture. height ( ) ) ;
335
286
graphics_state. bind_ui_texture ( texture) ;
@@ -343,31 +294,63 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
343
294
window. request_redraw ( ) ;
344
295
}
345
296
}
346
- CustomEvent :: ScheduleBrowserWork ( instant) => {
297
+ AppEvent :: ScheduleBrowserWork ( instant) => {
347
298
if instant <= Instant :: now ( ) {
348
299
self . cef_context . work ( ) ;
349
300
} else {
350
301
self . cef_schedule = Some ( instant) ;
351
302
}
352
303
}
353
- CustomEvent :: CloseWindow => {
304
+ AppEvent :: CloseWindow => {
354
305
// TODO: Implement graceful shutdown
355
306
356
307
tracing:: info!( "Exiting main event loop" ) ;
357
308
event_loop. exit ( ) ;
358
309
}
359
310
}
360
311
}
312
+ }
313
+ impl ApplicationHandler for App {
314
+ fn can_create_surfaces ( & mut self , event_loop : & dyn ActiveEventLoop ) {
315
+ let window_attributes = self . native_window . build ( event_loop) ;
316
+
317
+ let window: Arc < dyn Window > = Arc :: from ( event_loop. create_window ( window_attributes) . unwrap ( ) ) ;
361
318
362
- fn window_event ( & mut self , event_loop : & ActiveEventLoop , _window_id : WindowId , event : WindowEvent ) {
319
+ self . native_window . setup ( window. as_ref ( ) ) ;
320
+
321
+ let graphics_state = GraphicsState :: new ( window. clone ( ) , self . wgpu_context . clone ( ) ) ;
322
+
323
+ self . window = Some ( window) ;
324
+ self . graphics_state = Some ( graphics_state) ;
325
+
326
+ tracing:: info!( "Winit window created and ready" ) ;
327
+
328
+ self . desktop_wrapper . init ( self . wgpu_context . clone ( ) ) ;
329
+
330
+ #[ cfg( target_os = "windows" ) ]
331
+ let platform = Platform :: Windows ;
332
+ #[ cfg( target_os = "macos" ) ]
333
+ let platform = Platform :: Mac ;
334
+ #[ cfg( target_os = "linux" ) ]
335
+ let platform = Platform :: Linux ;
336
+ self . dispatch_desktop_wrapper_message ( DesktopWrapperMessage :: UpdatePlatform ( platform) ) ;
337
+ }
338
+
339
+ fn proxy_wake_up ( & mut self , event_loop : & dyn ActiveEventLoop ) {
340
+ while let Ok ( event) = self . app_event_receiver . try_recv ( ) {
341
+ self . user_event ( event_loop, event) ;
342
+ }
343
+ }
344
+
345
+ fn window_event ( & mut self , event_loop : & dyn ActiveEventLoop , _window_id : WindowId , event : WindowEvent ) {
363
346
self . cef_context . handle_window_event ( & event) ;
364
347
365
348
match event {
366
349
WindowEvent :: CloseRequested => {
367
- let _ = self . event_loop_proxy . send_event ( CustomEvent :: CloseWindow ) ;
350
+ self . app_event_scheduler . schedule ( AppEvent :: CloseWindow ) ;
368
351
}
369
- WindowEvent :: Resized ( PhysicalSize { width , height } ) => {
370
- let _ = self . window_size_sender . send ( WindowSize :: new ( width as usize , height as usize ) ) ;
352
+ WindowEvent :: SurfaceResized ( size ) => {
353
+ let _ = self . cef_window_size_sender . send ( size . into ( ) ) ;
371
354
self . cef_context . notify_of_resize ( ) ;
372
355
}
373
356
WindowEvent :: RedrawRequested => {
@@ -387,23 +370,44 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
387
370
let _ = self . start_render_sender . try_send ( ( ) ) ;
388
371
}
389
372
}
390
- // Currently not supported on wayland see https://github.com/rust-windowing/winit/issues/1881
391
- WindowEvent :: DroppedFile ( path) => {
392
- match std:: fs:: read ( & path) {
393
- Ok ( content) => {
394
- let message = DesktopWrapperMessage :: OpenFile { path, content } ;
395
- let _ = self . event_loop_proxy . send_event ( CustomEvent :: DesktopWrapperMessage ( message) ) ;
396
- }
397
- Err ( e) => {
398
- tracing:: error!( "Failed to read dropped file {}: {}" , path. display( ) , e) ;
399
- return ;
400
- }
401
- } ;
373
+ WindowEvent :: DragDropped { paths, .. } => {
374
+ for path in paths {
375
+ match std:: fs:: read ( & path) {
376
+ Ok ( content) => {
377
+ let message = DesktopWrapperMessage :: OpenFile { path, content } ;
378
+ self . app_event_scheduler . schedule ( AppEvent :: DesktopWrapperMessage ( message) ) ;
379
+ }
380
+ Err ( e) => {
381
+ tracing:: error!( "Failed to read dropped file {}: {}" , path. display( ) , e) ;
382
+ return ;
383
+ }
384
+ } ;
385
+ }
402
386
}
403
387
_ => { }
404
388
}
405
389
406
390
// Notify cef of possible input events
407
391
self . cef_context . work ( ) ;
408
392
}
393
+
394
+ fn about_to_wait ( & mut self , event_loop : & dyn ActiveEventLoop ) {
395
+ // Set a timeout in case we miss any cef schedule requests
396
+ let timeout = Instant :: now ( ) + Duration :: from_millis ( 10 ) ;
397
+ let wait_until = timeout. min ( self . cef_schedule . unwrap_or ( timeout) ) ;
398
+ if let Some ( schedule) = self . cef_schedule
399
+ && schedule < Instant :: now ( )
400
+ {
401
+ self . cef_schedule = None ;
402
+ // Poll cef message loop multiple times to avoid message loop starvation
403
+ for _ in 0 ..CEF_MESSAGE_LOOP_MAX_ITERATIONS {
404
+ self . cef_context . work ( ) ;
405
+ }
406
+ }
407
+ if let Some ( window) = & self . window . as_ref ( ) {
408
+ window. request_redraw ( ) ;
409
+ }
410
+
411
+ event_loop. set_control_flow ( ControlFlow :: WaitUntil ( wait_until) ) ;
412
+ }
409
413
}
0 commit comments