4
4
use std:: sync:: Arc ;
5
5
6
6
use egui_winit:: winit;
7
- use winit:: event_loop:: ControlFlow ;
7
+ use winit:: event_loop:: { ControlFlow , EventLoop } ;
8
8
9
9
use super :: epi_integration;
10
10
use crate :: epi;
11
11
12
+ #[ derive( Debug ) ]
12
13
struct RequestRepaintEvent ;
13
14
14
15
#[ cfg( feature = "glow" ) ]
15
16
#[ allow( unsafe_code) ]
16
17
fn create_display (
17
18
native_options : & NativeOptions ,
18
19
window_builder : winit:: window:: WindowBuilder ,
19
- event_loop : & winit :: event_loop :: EventLoop < RequestRepaintEvent > ,
20
+ event_loop : & EventLoop < RequestRepaintEvent > ,
20
21
) -> (
21
22
glutin:: WindowedContext < glutin:: PossiblyCurrent > ,
22
23
glow:: Context ,
@@ -63,6 +64,8 @@ enum EventResult {
63
64
/// Run an egui app
64
65
#[ cfg( feature = "glow" ) ]
65
66
mod glow_integration {
67
+ use std:: time:: { Duration , Instant } ;
68
+
66
69
use super :: * ;
67
70
68
71
struct GlowEframe {
@@ -76,7 +79,7 @@ mod glow_integration {
76
79
77
80
impl GlowEframe {
78
81
fn new (
79
- event_loop : & winit :: event_loop :: EventLoop < RequestRepaintEvent > ,
82
+ event_loop : & EventLoop < RequestRepaintEvent > ,
80
83
app_name : & str ,
81
84
native_options : & epi:: NativeOptions ,
82
85
app_creator : epi:: AppCreator ,
@@ -136,6 +139,13 @@ mod glow_integration {
136
139
}
137
140
}
138
141
142
+ fn save_and_destroy ( & mut self ) {
143
+ self . integration
144
+ . save ( & mut * self . app , self . gl_window . window ( ) ) ;
145
+ self . app . on_exit ( Some ( & self . gl ) ) ;
146
+ self . painter . destroy ( ) ;
147
+ }
148
+
139
149
fn paint ( & mut self ) -> ControlFlow {
140
150
#[ cfg( feature = "puffin" ) ]
141
151
puffin:: GlobalProfiler :: lock ( ) . new_frame ( ) ;
@@ -221,15 +231,6 @@ mod glow_integration {
221
231
}
222
232
223
233
fn on_event ( & mut self , event : winit:: event:: Event < ' _ , RequestRepaintEvent > ) -> EventResult {
224
- let Self {
225
- gl_window,
226
- gl,
227
- integration,
228
- painter,
229
- ..
230
- } = self ;
231
- let window = gl_window. window ( ) ;
232
-
233
234
match event {
234
235
// Platform-dependent event handlers to workaround a winit bug
235
236
// See: https://github.com/rust-windowing/winit/issues/987
@@ -247,39 +248,39 @@ mod glow_integration {
247
248
// See: https://github.com/rust-windowing/winit/issues/208
248
249
// This solves an issue where the app would panic when minimizing on Windows.
249
250
if physical_size. width > 0 && physical_size. height > 0 {
250
- gl_window. resize ( * physical_size) ;
251
+ self . gl_window . resize ( * physical_size) ;
251
252
}
252
253
}
253
254
winit:: event:: WindowEvent :: ScaleFactorChanged {
254
255
new_inner_size, ..
255
256
} => {
256
- gl_window. resize ( * * new_inner_size) ;
257
+ self . gl_window . resize ( * * new_inner_size) ;
257
258
}
258
- winit:: event:: WindowEvent :: CloseRequested if integration. should_quit ( ) => {
259
+ winit:: event:: WindowEvent :: CloseRequested
260
+ if self . integration . should_quit ( ) =>
261
+ {
259
262
return EventResult :: Exit
260
263
}
261
264
_ => { }
262
265
}
263
266
264
- integration. on_event ( self . app . as_mut ( ) , & event) ;
265
- if integration. should_quit ( ) {
267
+ self . integration . on_event ( self . app . as_mut ( ) , & event) ;
268
+
269
+ if self . integration . should_quit ( ) {
266
270
EventResult :: Exit
267
271
} else {
268
- window. request_redraw ( ) ; // TODO(emilk): ask egui if the events warrants a repaint instead
272
+ self . gl_window . window ( ) . request_redraw ( ) ; // TODO(emilk): ask egui if the event warrants a repaint
269
273
EventResult :: Continue
270
274
}
271
275
}
272
276
winit:: event:: Event :: LoopDestroyed => {
273
- integration. save ( & mut * self . app , window) ;
274
- self . app . on_exit ( Some ( gl) ) ;
275
- painter. destroy ( ) ;
276
- EventResult :: Continue
277
+ unreachable ! ( "Should be handled outside this function!" )
277
278
}
278
279
winit:: event:: Event :: UserEvent ( RequestRepaintEvent )
279
280
| winit:: event:: Event :: NewEvents ( winit:: event:: StartCause :: ResumeTimeReached {
280
281
..
281
282
} ) => {
282
- window. request_redraw ( ) ;
283
+ self . gl_window . window ( ) . request_redraw ( ) ;
283
284
EventResult :: Continue
284
285
}
285
286
_ => EventResult :: Continue ,
@@ -291,19 +292,108 @@ mod glow_integration {
291
292
app_name : & str ,
292
293
native_options : & epi:: NativeOptions ,
293
294
app_creator : epi:: AppCreator ,
294
- ) -> ! {
295
- let event_loop = winit:: event_loop:: EventLoop :: with_user_event ( ) ;
296
- let mut glow_eframe = GlowEframe :: new ( & event_loop, app_name, native_options, app_creator) ;
295
+ ) {
296
+ let event_loop = EventLoop :: with_user_event ( ) ;
297
+ let glow_eframe = GlowEframe :: new ( & event_loop, app_name, native_options, app_creator) ;
298
+
299
+ if native_options. exit_on_window_close {
300
+ run_then_exit ( event_loop, glow_eframe) ;
301
+ } else {
302
+ run_and_continue ( event_loop, glow_eframe) ;
303
+ }
304
+ }
297
305
298
- event_loop. run ( move |event, _, control_flow| {
299
- let event_result = glow_eframe. on_event ( event) ;
300
- match event_result {
301
- EventResult :: Continue => { }
302
- EventResult :: Repaint => {
303
- * control_flow = glow_eframe. paint ( ) ;
306
+ fn suggest_sleep_duration ( glow_eframe : & GlowEframe ) -> Duration {
307
+ if glow_eframe. is_focused || glow_eframe. integration . files_are_hovering ( ) {
308
+ Duration :: from_millis ( 10 )
309
+ } else {
310
+ Duration :: from_millis ( 50 )
311
+ }
312
+ }
313
+
314
+ fn run_and_continue (
315
+ mut event_loop : EventLoop < RequestRepaintEvent > ,
316
+ mut glow_eframe : GlowEframe ,
317
+ ) {
318
+ let mut running = true ;
319
+ let mut needs_repaint_by = Instant :: now ( ) ;
320
+
321
+ while running {
322
+ use winit:: platform:: run_return:: EventLoopExtRunReturn as _;
323
+ event_loop. run_return ( |event, _, control_flow| {
324
+ * control_flow = match event {
325
+ winit:: event:: Event :: LoopDestroyed => ControlFlow :: Exit ,
326
+ winit:: event:: Event :: MainEventsCleared => ControlFlow :: Wait ,
327
+ event => {
328
+ let event_result = glow_eframe. on_event ( event) ;
329
+ match event_result {
330
+ EventResult :: Continue => ControlFlow :: Wait ,
331
+ EventResult :: Repaint => {
332
+ needs_repaint_by = Instant :: now ( ) ;
333
+ ControlFlow :: Exit
334
+ }
335
+ EventResult :: Exit => {
336
+ running = false ;
337
+ ControlFlow :: Exit
338
+ }
339
+ }
340
+ }
341
+ } ;
342
+
343
+ match needs_repaint_by. checked_duration_since ( Instant :: now ( ) ) {
344
+ None => {
345
+ * control_flow = ControlFlow :: Exit ; // Time to redraw
346
+ }
347
+ Some ( duration_until_repaint) => {
348
+ if * control_flow == ControlFlow :: Wait {
349
+ // On Mac, ControlFlow::WaitUntil doesn't sleep enough. It uses a lot of CPU.
350
+ // So we sleep manually. But, it still uses 1-3% CPU :(
351
+ let sleep_duration =
352
+ duration_until_repaint. min ( suggest_sleep_duration ( & glow_eframe) ) ;
353
+ std:: thread:: sleep ( sleep_duration) ;
354
+
355
+ * control_flow = ControlFlow :: WaitUntil ( needs_repaint_by) ;
356
+ }
357
+ }
358
+ }
359
+ } ) ;
360
+
361
+ if running && needs_repaint_by <= Instant :: now ( ) {
362
+ let paint_result = glow_eframe. paint ( ) ;
363
+ match paint_result {
364
+ ControlFlow :: Poll => {
365
+ needs_repaint_by = Instant :: now ( ) ;
366
+ }
367
+ ControlFlow :: Wait => {
368
+ // wait a long time unless something happens
369
+ needs_repaint_by = Instant :: now ( ) + Duration :: from_secs ( 3600 ) ;
370
+ }
371
+ ControlFlow :: WaitUntil ( repaint_time) => {
372
+ needs_repaint_by = repaint_time;
373
+ }
374
+ ControlFlow :: Exit => {
375
+ running = false ;
376
+ }
304
377
}
305
- EventResult :: Exit => {
306
- * control_flow = ControlFlow :: Exit ;
378
+ }
379
+ }
380
+ glow_eframe. save_and_destroy ( ) ;
381
+ }
382
+
383
+ fn run_then_exit ( event_loop : EventLoop < RequestRepaintEvent > , mut glow_eframe : GlowEframe ) -> ! {
384
+ event_loop. run ( move |event, _, control_flow| {
385
+ if let winit:: event:: Event :: LoopDestroyed = event {
386
+ glow_eframe. save_and_destroy ( ) ;
387
+ } else {
388
+ let event_result = glow_eframe. on_event ( event) ;
389
+ match event_result {
390
+ EventResult :: Continue => { }
391
+ EventResult :: Repaint => {
392
+ * control_flow = glow_eframe. paint ( ) ;
393
+ }
394
+ EventResult :: Exit => {
395
+ * control_flow = ControlFlow :: Exit ;
396
+ }
307
397
}
308
398
}
309
399
} )
@@ -325,7 +415,7 @@ pub fn run_wgpu(
325
415
) -> ! {
326
416
let storage = epi_integration:: create_storage ( app_name) ;
327
417
let window_settings = epi_integration:: load_window_settings ( storage. as_deref ( ) ) ;
328
- let event_loop = winit :: event_loop :: EventLoop :: with_user_event ( ) ;
418
+ let event_loop = EventLoop :: with_user_event ( ) ;
329
419
330
420
let window = epi_integration:: window_builder ( native_options, & window_settings)
331
421
. with_title ( app_name)
@@ -491,7 +581,7 @@ pub fn run_wgpu(
491
581
if integration. should_quit ( ) {
492
582
* control_flow = winit:: event_loop:: ControlFlow :: Exit ;
493
583
}
494
- window. request_redraw ( ) ; // TODO(emilk): ask egui if the events warrants a repaint instead
584
+ window. request_redraw ( ) ; // TODO(emilk): ask egui if the event warrants a repaint
495
585
}
496
586
winit:: event:: Event :: LoopDestroyed => {
497
587
integration. save ( & mut * app, window) ;
0 commit comments