Skip to content

Commit 04b3921

Browse files
authored
egui_glium: run app code outside event loop to fix file dialogs (#631)
Previously app code was run from within the event loop which lead to file dialogs (e.g. using nfd2) to hang (see rust-windowing/winit#1779) Now egui_glium polls for events and then runs the app code.
1 parent 661f0d7 commit 04b3921

File tree

4 files changed

+83
-54
lines changed

4 files changed

+83
-54
lines changed

eframe/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to the `eframe` crate.
44

55
## Unreleased
66
* Improve http fetch API.
7+
* `run_native` now returns when the app is closed.
78

89

910
## 0.13.1 - 2021-06-24

eframe/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,6 @@ pub fn start_web(canvas_id: &str, app: Box<dyn epi::App>) -> Result<(), wasm_bin
6868

6969
/// Call from `fn main` like this: `eframe::run_native(Box::new(MyEguiApp::default()))`
7070
#[cfg(not(target_arch = "wasm32"))]
71-
pub fn run_native(app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> ! {
71+
pub fn run_native(app: Box<dyn epi::App>, native_options: epi::NativeOptions) {
7272
egui_glium::run(app, native_options)
7373
}

egui_glium/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to the `egui_glium` integration will be noted in this file.
44

55

66
## Unreleased
7+
* Fix native file dialogs hanging (eg. when using [`nfd2`](https://github.com/EmbarkStudios/nfd2)
78
* [Fix minimize on Windows](https://github.com/emilk/egui/issues/518)
89
* Change `drag_and_drop_support` to `false` by default (Windows only). See <https://github.com/emilk/egui/issues/598>.
910
* Don't restore window position on Windows, because the position would sometimes be invalid.

egui_glium/src/backend.rs

+80-53
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,15 @@ fn load_icon(icon_data: epi::IconData) -> Option<glutin::window::Icon> {
172172
// ----------------------------------------------------------------------------
173173

174174
/// Run an egui app
175-
pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> ! {
175+
pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) {
176176
#[allow(unused_mut)]
177177
let mut storage = create_storage(app.name());
178178

179179
#[cfg(feature = "http")]
180180
let http = std::sync::Arc::new(crate::http::GliumHttp {});
181181

182182
let window_settings = deserialize_window_settings(&storage);
183-
let event_loop = glutin::event_loop::EventLoop::with_user_event();
183+
let mut event_loop = glutin::event_loop::EventLoop::with_user_event();
184184
let icon = native_options.icon_data.clone().and_then(load_icon);
185185
let display = create_display(&*app, &native_options, window_settings, icon, &event_loop);
186186

@@ -208,8 +208,6 @@ pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> !
208208

209209
let mut previous_frame_time = None;
210210

211-
let mut is_focused = true;
212-
213211
#[cfg(feature = "persistence")]
214212
let mut last_auto_save = Instant::now();
215213

@@ -241,8 +239,67 @@ pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> !
241239
// eprintln!("Warmed up in {} ms", warm_up_start.elapsed().as_millis())
242240
}
243241

244-
event_loop.run(move |event, _, control_flow| {
245-
let mut redraw = || {
242+
let mut is_focused = true;
243+
let mut running = true;
244+
let mut repaint_asap = true;
245+
246+
while running {
247+
use glium::glutin::platform::run_return::EventLoopExtRunReturn as _;
248+
event_loop.run_return(|event, _, control_flow| {
249+
use glium::glutin::event_loop::ControlFlow;
250+
251+
*control_flow = ControlFlow::Wait;
252+
253+
match event {
254+
// Platform-dependent event handlers to workaround a winit bug
255+
// See: https://github.com/rust-windowing/winit/issues/987
256+
// See: https://github.com/rust-windowing/winit/issues/1619
257+
glutin::event::Event::RedrawEventsCleared if cfg!(windows) => {
258+
*control_flow = ControlFlow::Exit; // Time to redraw
259+
}
260+
glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => {
261+
*control_flow = ControlFlow::Exit; // Time to redraw
262+
}
263+
glutin::event::Event::MainEventsCleared => {
264+
if repaint_asap {
265+
*control_flow = ControlFlow::Exit; // Time to redraw
266+
} else {
267+
// Winit uses up all the CPU of one core when returning ControlFlow::Wait.
268+
// Sleeping here helps, but still uses 1-3% of CPU :(
269+
if is_focused {
270+
std::thread::sleep(std::time::Duration::from_millis(10));
271+
} else {
272+
std::thread::sleep(std::time::Duration::from_millis(100));
273+
}
274+
}
275+
}
276+
glutin::event::Event::WindowEvent { event, .. } => {
277+
if egui.is_quit_event(&event) {
278+
*control_flow = ControlFlow::Exit;
279+
running = false;
280+
}
281+
282+
if let glutin::event::WindowEvent::Focused(new_focused) = event {
283+
is_focused = new_focused;
284+
}
285+
286+
egui.on_event(&event);
287+
288+
// TODO: ask egui if the events warrants a repaint instead of repainting on each event.
289+
display.gl_window().window().request_redraw();
290+
}
291+
glutin::event::Event::UserEvent(RequestRepaintEvent) => {
292+
display.gl_window().window().request_redraw();
293+
*control_flow = ControlFlow::Exit; // Time to redraw
294+
}
295+
296+
_ => (),
297+
}
298+
});
299+
300+
repaint_asap = false;
301+
302+
if running {
246303
if !is_focused {
247304
// On Mac, a minimized Window uses up all CPU: https://github.com/emilk/egui/issues/325
248305
// We can't know if we are minimized: https://github.com/rust-windowing/winit/issues/208
@@ -299,13 +356,11 @@ pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> !
299356
);
300357
}
301358

302-
*control_flow = if quit {
303-
glutin::event_loop::ControlFlow::Exit
359+
if quit {
360+
running = false;
304361
} else if needs_repaint {
305362
display.gl_window().window().request_redraw();
306-
glutin::event_loop::ControlFlow::Poll
307-
} else {
308-
glutin::event_loop::ControlFlow::Wait
363+
repaint_asap = true;
309364
};
310365
}
311366

@@ -324,48 +379,20 @@ pub fn run(mut app: Box<dyn epi::App>, native_options: epi::NativeOptions) -> !
324379
last_auto_save = now;
325380
}
326381
}
327-
};
328-
329-
match event {
330-
// Platform-dependent event handlers to workaround a winit bug
331-
// See: https://github.com/rust-windowing/winit/issues/987
332-
// See: https://github.com/rust-windowing/winit/issues/1619
333-
glutin::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
334-
glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),
335-
336-
glutin::event::Event::WindowEvent { event, .. } => {
337-
if egui.is_quit_event(&event) {
338-
*control_flow = glium::glutin::event_loop::ControlFlow::Exit;
339-
}
340-
341-
if let glutin::event::WindowEvent::Focused(new_focused) = event {
342-
is_focused = new_focused;
343-
}
344-
345-
egui.on_event(&event);
346-
347-
display.gl_window().window().request_redraw(); // TODO: ask egui if the events warrants a repaint instead
348-
}
349-
glutin::event::Event::LoopDestroyed => {
350-
app.on_exit();
351-
#[cfg(feature = "persistence")]
352-
if let Some(storage) = &mut storage {
353-
epi::set_value(
354-
storage.as_mut(),
355-
WINDOW_KEY,
356-
&WindowSettings::from_display(&display),
357-
);
358-
epi::set_value(storage.as_mut(), EGUI_MEMORY_KEY, &*egui.ctx().memory());
359-
app.save(storage.as_mut());
360-
storage.flush();
361-
}
362-
}
382+
}
383+
}
363384

364-
glutin::event::Event::UserEvent(RequestRepaintEvent) => {
365-
display.gl_window().window().request_redraw();
366-
}
385+
app.on_exit();
367386

368-
_ => (),
369-
}
370-
});
387+
#[cfg(feature = "persistence")]
388+
if let Some(storage) = &mut storage {
389+
epi::set_value(
390+
storage.as_mut(),
391+
WINDOW_KEY,
392+
&WindowSettings::from_display(&display),
393+
);
394+
epi::set_value(storage.as_mut(), EGUI_MEMORY_KEY, &*egui.ctx().memory());
395+
app.save(storage.as_mut());
396+
storage.flush();
397+
}
371398
}

0 commit comments

Comments
 (0)