mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 07:03:14 -04:00
eframe error handling (#2433)
* eframe::run_native: return errors instead of crashing * Detect and handle glutin errors * egui_demo_app: silence wgpu log spam * Add trace logs for why eframe is shutting down * Fix: only save App state once on Mac * Handle Winit failure * Log where we load app state from * Don't panic on zero-sized window * Clamp loaded window size to not be too tiny to see * Simplify code: more shared code in window_builder * Improve code readability * Fix wasm32 build * fix android * Update changelog
This commit is contained in:
@@ -708,6 +708,7 @@ impl Frame {
|
||||
#[doc(alias = "exit")]
|
||||
#[doc(alias = "quit")]
|
||||
pub fn close(&mut self) {
|
||||
tracing::debug!("eframe::Frame::close called");
|
||||
self.output.close = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ pub async fn start_web(
|
||||
canvas_id: &str,
|
||||
web_options: WebOptions,
|
||||
app_creator: AppCreator,
|
||||
) -> Result<AppRunnerRef, wasm_bindgen::JsValue> {
|
||||
) -> std::result::Result<AppRunnerRef, wasm_bindgen::JsValue> {
|
||||
let handle = web::start(canvas_id, web_options, app_creator).await?;
|
||||
|
||||
Ok(handle)
|
||||
@@ -174,9 +174,16 @@ mod native;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
/// This function can fail if we fail to set up a graphics context.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: AppCreator) {
|
||||
pub fn run_native(
|
||||
app_name: &str,
|
||||
native_options: NativeOptions,
|
||||
app_creator: AppCreator,
|
||||
) -> Result<()> {
|
||||
let renderer = native_options.renderer;
|
||||
|
||||
#[cfg(not(feature = "__screenshot"))]
|
||||
@@ -189,17 +196,41 @@ pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: Ap
|
||||
#[cfg(feature = "glow")]
|
||||
Renderer::Glow => {
|
||||
tracing::debug!("Using the glow renderer");
|
||||
native::run::run_glow(app_name, native_options, app_creator);
|
||||
native::run::run_glow(app_name, native_options, app_creator)
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
Renderer::Wgpu => {
|
||||
tracing::debug!("Using the wgpu renderer");
|
||||
native::run::run_wgpu(app_name, native_options, app_creator);
|
||||
native::run::run_wgpu(app_name, native_options, app_creator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// The different problems that can occur when trying to run `eframe`.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum EframeError {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("winit error: {0}")]
|
||||
Winit(#[from] winit::error::OsError),
|
||||
|
||||
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||
#[error("glutin error: {0}")]
|
||||
Glutin(#[from] glutin::error::Error),
|
||||
|
||||
#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
|
||||
#[error("Found no glutin configs matching the template: {0:?}")]
|
||||
NoGlutinConfigs(glutin::config::ConfigTemplate),
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
#[error("WGPU error: {0}")]
|
||||
Wgpu(#[from] wgpu::RequestDeviceError),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, EframeError>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Profiling macro for feature "puffin"
|
||||
|
||||
@@ -49,6 +49,7 @@ pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -
|
||||
}
|
||||
|
||||
pub fn window_builder(
|
||||
title: &str,
|
||||
native_options: &epi::NativeOptions,
|
||||
window_settings: &Option<WindowSettings>,
|
||||
) -> winit::window::WindowBuilder {
|
||||
@@ -73,13 +74,17 @@ pub fn window_builder(
|
||||
let window_icon = icon_data.clone().and_then(load_icon);
|
||||
|
||||
let mut window_builder = winit::window::WindowBuilder::new()
|
||||
.with_title(title)
|
||||
.with_always_on_top(*always_on_top)
|
||||
.with_decorations(*decorated)
|
||||
.with_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None)))
|
||||
.with_maximized(*maximized)
|
||||
.with_resizable(*resizable)
|
||||
.with_transparent(*transparent)
|
||||
.with_window_icon(window_icon);
|
||||
.with_window_icon(window_icon)
|
||||
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
||||
// We must also keep the window hidden until AccessKit is initialized.
|
||||
.with_visible(false);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if *fullsize_content {
|
||||
@@ -308,8 +313,15 @@ impl EpiIntegration {
|
||||
use winit::event::{ElementState, MouseButton, WindowEvent};
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => self.close = app.on_close_event(),
|
||||
WindowEvent::Destroyed => self.close = true,
|
||||
WindowEvent::CloseRequested => {
|
||||
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||
self.close = app.on_close_event();
|
||||
tracing::debug!("App::on_close_event returned {}", self.close);
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
tracing::debug!("Received WindowEvent::Destroyed");
|
||||
self.close = true;
|
||||
}
|
||||
WindowEvent::MouseInput {
|
||||
button: MouseButton::Left,
|
||||
state: ElementState::Pressed,
|
||||
@@ -351,6 +363,7 @@ impl EpiIntegration {
|
||||
self.can_drag_window = false;
|
||||
if app_output.close {
|
||||
self.close = app.on_close_event();
|
||||
tracing::debug!("App::on_close_event returned {}", self.close);
|
||||
}
|
||||
self.frame.output.visible = app_output.visible; // this is handled by post_present
|
||||
handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
|
||||
@@ -432,7 +445,9 @@ const STORAGE_WINDOW_KEY: &str = "window";
|
||||
pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> {
|
||||
#[cfg(feature = "persistence")]
|
||||
{
|
||||
epi::get_value(_storage?, STORAGE_WINDOW_KEY)
|
||||
let mut settings: WindowSettings = epi::get_value(_storage?, STORAGE_WINDOW_KEY)?;
|
||||
settings.clamp_to_sane_values();
|
||||
Some(settings)
|
||||
}
|
||||
#[cfg(not(feature = "persistence"))]
|
||||
None
|
||||
|
||||
@@ -26,6 +26,7 @@ impl FileStorage {
|
||||
/// Store the state in this .ron file.
|
||||
pub fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
|
||||
let ron_filepath: PathBuf = ron_filepath.into();
|
||||
tracing::debug!("Loading app state from {:?}…", ron_filepath);
|
||||
Self {
|
||||
kv: read_ron(&ron_filepath).unwrap_or_default(),
|
||||
ron_filepath,
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
|
||||
//! When making changes to one you often also want to apply it to the other.
|
||||
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "accesskit")]
|
||||
use egui_winit::accesskit_winit;
|
||||
use egui_winit::winit;
|
||||
use winit::event_loop::{
|
||||
ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget,
|
||||
};
|
||||
|
||||
#[cfg(feature = "accesskit")]
|
||||
use egui_winit::accesskit_winit;
|
||||
use egui_winit::winit;
|
||||
|
||||
use crate::{epi, Result};
|
||||
|
||||
use super::epi_integration::{self, EpiIntegration};
|
||||
use crate::epi;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UserEvent {
|
||||
@@ -60,7 +63,7 @@ trait WinitApp {
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
) -> EventResult;
|
||||
) -> Result<EventResult>;
|
||||
}
|
||||
|
||||
fn create_event_loop_builder(
|
||||
@@ -79,10 +82,10 @@ fn create_event_loop_builder(
|
||||
///
|
||||
/// We reuse the event-loop so we can support closing and opening an eframe window
|
||||
/// multiple times. This is just a limitation of winit.
|
||||
fn with_event_loop(
|
||||
fn with_event_loop<R>(
|
||||
mut native_options: epi::NativeOptions,
|
||||
f: impl FnOnce(&mut EventLoop<UserEvent>, NativeOptions),
|
||||
) {
|
||||
f: impl FnOnce(&mut EventLoop<UserEvent>, NativeOptions) -> R,
|
||||
) -> R {
|
||||
use std::cell::RefCell;
|
||||
thread_local!(static EVENT_LOOP: RefCell<Option<EventLoop<UserEvent>>> = RefCell::new(None));
|
||||
|
||||
@@ -93,22 +96,31 @@ fn with_event_loop(
|
||||
let mut event_loop = event_loop.borrow_mut();
|
||||
let event_loop = event_loop
|
||||
.get_or_insert_with(|| create_event_loop_builder(&mut native_options).build());
|
||||
f(event_loop, native_options);
|
||||
});
|
||||
f(event_loop, native_options)
|
||||
})
|
||||
}
|
||||
|
||||
fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl WinitApp) {
|
||||
fn run_and_return(
|
||||
event_loop: &mut EventLoop<UserEvent>,
|
||||
mut winit_app: impl WinitApp,
|
||||
) -> Result<()> {
|
||||
use winit::platform::run_return::EventLoopExtRunReturn as _;
|
||||
|
||||
tracing::debug!("event_loop.run_return");
|
||||
tracing::debug!("Entering the winit event loop (run_return)…");
|
||||
|
||||
let mut next_repaint_time = Instant::now();
|
||||
|
||||
let mut returned_result = Ok(());
|
||||
|
||||
event_loop.run_return(|event, event_loop, control_flow| {
|
||||
let event_result = match &event {
|
||||
winit::event::Event::LoopDestroyed => {
|
||||
tracing::debug!("winit::event::Event::LoopDestroyed");
|
||||
EventResult::Exit
|
||||
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
||||
// so we need to save state now:
|
||||
tracing::debug!("Received Event::LoopDestroyed - saving app state…");
|
||||
winit_app.save_and_destroy();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Platform-dependent event handlers to workaround a winit bug
|
||||
@@ -137,7 +149,14 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
event => winit_app.on_event(event_loop, event),
|
||||
event => match winit_app.on_event(event_loop, event) {
|
||||
Ok(event_result) => event_result,
|
||||
Err(err) => {
|
||||
returned_result = Err(err);
|
||||
tracing::debug!("Exiting because of an error");
|
||||
EventResult::Exit
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
match event_result {
|
||||
@@ -155,10 +174,7 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||
next_repaint_time = next_repaint_time.min(repaint_time);
|
||||
}
|
||||
EventResult::Exit => {
|
||||
// On Cmd-Q we get here and then `run_return` doesn't return,
|
||||
// so we need to save state now:
|
||||
tracing::debug!("Exiting event loop - saving app state…");
|
||||
winit_app.save_and_destroy();
|
||||
tracing::debug!("Asking to exit event loop…");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
@@ -187,16 +203,21 @@ fn run_and_return(event_loop: &mut EventLoop<UserEvent>, mut winit_app: impl Win
|
||||
event_loop.run_return(|_, _, control_flow| {
|
||||
control_flow.set_exit();
|
||||
});
|
||||
|
||||
returned_result
|
||||
}
|
||||
|
||||
fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp + 'static) -> ! {
|
||||
tracing::debug!("event_loop.run");
|
||||
tracing::debug!("Entering the winit event loop (run)…");
|
||||
|
||||
let mut next_repaint_time = Instant::now();
|
||||
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
let event_result = match event {
|
||||
winit::event::Event::LoopDestroyed => EventResult::Exit,
|
||||
winit::event::Event::LoopDestroyed => {
|
||||
tracing::debug!("Received Event::LoopDestroyed");
|
||||
EventResult::Exit
|
||||
}
|
||||
|
||||
// Platform-dependent event handlers to workaround a winit bug
|
||||
// See: https://github.com/rust-windowing/winit/issues/987
|
||||
@@ -215,7 +236,12 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
..
|
||||
}) => EventResult::RepaintNext,
|
||||
|
||||
event => winit_app.on_event(event_loop, &event),
|
||||
event => match winit_app.on_event(event_loop, &event) {
|
||||
Ok(event_result) => event_result,
|
||||
Err(err) => {
|
||||
panic!("eframe encountered a fatal error: {err}");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
match event_result {
|
||||
@@ -231,7 +257,7 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
next_repaint_time = next_repaint_time.min(repaint_time);
|
||||
}
|
||||
EventResult::Exit => {
|
||||
tracing::debug!("Quitting…");
|
||||
tracing::debug!("Quitting - saving app state…");
|
||||
winit_app.save_and_destroy();
|
||||
#[allow(clippy::exit)]
|
||||
std::process::exit(0);
|
||||
@@ -279,6 +305,8 @@ fn center_window_pos(
|
||||
mod glow_integration {
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::NumExt as _;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Note: that the current Glutin API design tightly couples the GL context with
|
||||
@@ -321,7 +349,7 @@ mod glow_integration {
|
||||
unsafe fn new(
|
||||
winit_window: winit::window::Window,
|
||||
native_options: &epi::NativeOptions,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
use glutin::prelude::*;
|
||||
use raw_window_handle::*;
|
||||
let hardware_acceleration = match native_options.hardware_acceleration {
|
||||
@@ -351,8 +379,7 @@ mod glow_integration {
|
||||
#[cfg(target_os = "android")]
|
||||
let preference = glutin::display::DisplayApiPreference::Egl;
|
||||
|
||||
let gl_display = glutin::display::Display::new(raw_display_handle, preference)
|
||||
.expect("failed to create glutin display");
|
||||
let gl_display = glutin::display::Display::new(raw_display_handle, preference)?;
|
||||
let swap_interval = if native_options.vsync {
|
||||
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
|
||||
} else {
|
||||
@@ -383,64 +410,49 @@ mod glow_integration {
|
||||
// options required by user like multi sampling, srgb, transparency etc..
|
||||
// TODO: need to figure out a good fallback config template
|
||||
let config = gl_display
|
||||
.find_configs(config_template)
|
||||
.expect("failed to find even a single matching configuration")
|
||||
.find_configs(config_template.clone())?
|
||||
.next()
|
||||
.expect("failed to find a matching configuration for creating opengl context");
|
||||
.ok_or(crate::EframeError::NoGlutinConfigs(config_template))?;
|
||||
|
||||
let context_attributes =
|
||||
glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
|
||||
// for surface creation.
|
||||
let (width, height): (u32, u32) = winit_window.inner_size().into();
|
||||
let width = std::num::NonZeroU32::new(width.at_least(1)).unwrap();
|
||||
let height = std::num::NonZeroU32::new(height.at_least(1)).unwrap();
|
||||
let surface_attributes =
|
||||
glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
|
||||
.build(
|
||||
raw_window_handle,
|
||||
std::num::NonZeroU32::new(width).unwrap(),
|
||||
std::num::NonZeroU32::new(height).unwrap(),
|
||||
);
|
||||
.build(raw_window_handle, width, height);
|
||||
// start creating the gl objects
|
||||
let gl_context = gl_display
|
||||
.create_context(&config, &context_attributes)
|
||||
.expect("failed to create opengl context");
|
||||
let gl_context = gl_display.create_context(&config, &context_attributes)?;
|
||||
|
||||
let gl_surface = gl_display
|
||||
.create_window_surface(&config, &surface_attributes)
|
||||
.expect("failed to create glutin window surface");
|
||||
let gl_context = gl_context
|
||||
.make_current(&gl_surface)
|
||||
.expect("failed to make gl context current");
|
||||
gl_surface
|
||||
.set_swap_interval(&gl_context, swap_interval)
|
||||
.expect("failed to set vsync swap interval");
|
||||
GlutinWindowContext {
|
||||
let gl_surface = gl_display.create_window_surface(&config, &surface_attributes)?;
|
||||
let gl_context = gl_context.make_current(&gl_surface)?;
|
||||
gl_surface.set_swap_interval(&gl_context, swap_interval)?;
|
||||
Ok(GlutinWindowContext {
|
||||
window: winit_window,
|
||||
gl_context,
|
||||
gl_display,
|
||||
gl_surface,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn window(&self) -> &winit::window::Window {
|
||||
&self.window
|
||||
}
|
||||
|
||||
fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
|
||||
use glutin::surface::GlSurface;
|
||||
self.gl_surface.resize(
|
||||
&self.gl_context,
|
||||
physical_size
|
||||
.width
|
||||
.try_into()
|
||||
.expect("physical size must not be zero"),
|
||||
physical_size
|
||||
.height
|
||||
.try_into()
|
||||
.expect("physical size must not be zero"),
|
||||
);
|
||||
let width = std::num::NonZeroU32::new(physical_size.width.at_least(1)).unwrap();
|
||||
let height = std::num::NonZeroU32::new(physical_size.height.at_least(1)).unwrap();
|
||||
self.gl_surface.resize(&self.gl_context, width, height);
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) -> glutin::error::Result<()> {
|
||||
use glutin::surface::GlSurface;
|
||||
self.gl_surface.swap_buffers(&self.gl_context)
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void {
|
||||
use glutin::display::GlDisplay;
|
||||
self.gl_display.get_proc_address(addr)
|
||||
@@ -484,25 +496,19 @@ mod glow_integration {
|
||||
fn create_glutin_windowed_context(
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
storage: Option<&dyn epi::Storage>,
|
||||
title: &String,
|
||||
title: &str,
|
||||
native_options: &NativeOptions,
|
||||
) -> (GlutinWindowContext, glow::Context) {
|
||||
) -> Result<(GlutinWindowContext, glow::Context)> {
|
||||
crate::profile_function!();
|
||||
|
||||
let window_settings = epi_integration::load_window_settings(storage);
|
||||
|
||||
let window_builder = epi_integration::window_builder(native_options, &window_settings)
|
||||
.with_title(title)
|
||||
.with_transparent(native_options.transparent)
|
||||
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
||||
// We must also keep the window hidden until AccessKit is initialized.
|
||||
.with_visible(false);
|
||||
let winit_window = window_builder
|
||||
.build(event_loop)
|
||||
.expect("failed to create winit window");
|
||||
let winit_window =
|
||||
epi_integration::window_builder(title, native_options, &window_settings)
|
||||
.build(event_loop)?;
|
||||
// a lot of the code below has been lifted from glutin example in their repo.
|
||||
let glutin_window_context =
|
||||
unsafe { GlutinWindowContext::new(winit_window, native_options) };
|
||||
unsafe { GlutinWindowContext::new(winit_window, native_options)? };
|
||||
let gl = unsafe {
|
||||
glow::Context::from_loader_function(|s| {
|
||||
let s = std::ffi::CString::new(s)
|
||||
@@ -512,10 +518,10 @@ mod glow_integration {
|
||||
})
|
||||
};
|
||||
|
||||
(glutin_window_context, gl)
|
||||
Ok((glutin_window_context, gl))
|
||||
}
|
||||
|
||||
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
||||
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
||||
let storage = epi_integration::create_storage(&self.app_name);
|
||||
|
||||
let (gl_window, gl) = Self::create_glutin_windowed_context(
|
||||
@@ -523,7 +529,7 @@ mod glow_integration {
|
||||
storage.as_deref(),
|
||||
&self.app_name,
|
||||
&self.native_options,
|
||||
);
|
||||
)?;
|
||||
let gl = Arc::new(gl);
|
||||
|
||||
let painter =
|
||||
@@ -585,6 +591,8 @@ mod glow_integration {
|
||||
integration,
|
||||
app,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,11 +734,11 @@ mod glow_integration {
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
) -> EventResult {
|
||||
match event {
|
||||
) -> Result<EventResult> {
|
||||
Ok(match event {
|
||||
winit::event::Event::Resumed => {
|
||||
if self.running.is_none() {
|
||||
self.init_run_state(event_loop);
|
||||
self.init_run_state(event_loop)?;
|
||||
}
|
||||
EventResult::RepaintNow
|
||||
}
|
||||
@@ -793,7 +801,8 @@ mod glow_integration {
|
||||
winit::event::WindowEvent::CloseRequested
|
||||
if running.integration.should_close() =>
|
||||
{
|
||||
return EventResult::Exit
|
||||
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||
return Ok(EventResult::Exit);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -832,7 +841,7 @@ mod glow_integration {
|
||||
}
|
||||
}
|
||||
_ => EventResult::Wait,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +849,7 @@ mod glow_integration {
|
||||
app_name: &str,
|
||||
mut native_options: epi::NativeOptions,
|
||||
app_creator: epi::AppCreator,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if native_options.run_and_return {
|
||||
with_event_loop(native_options, |event_loop, mut native_options| {
|
||||
if native_options.centered {
|
||||
@@ -849,8 +858,8 @@ mod glow_integration {
|
||||
|
||||
let glow_eframe =
|
||||
GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||
run_and_return(event_loop, glow_eframe);
|
||||
});
|
||||
run_and_return(event_loop, glow_eframe)
|
||||
})
|
||||
} else {
|
||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
||||
|
||||
@@ -923,38 +932,40 @@ mod wgpu_integration {
|
||||
fn create_window(
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
storage: Option<&dyn epi::Storage>,
|
||||
title: &String,
|
||||
title: &str,
|
||||
native_options: &NativeOptions,
|
||||
) -> winit::window::Window {
|
||||
) -> Result<winit::window::Window> {
|
||||
let window_settings = epi_integration::load_window_settings(storage);
|
||||
epi_integration::window_builder(native_options, &window_settings)
|
||||
.with_title(title)
|
||||
// Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
||||
// We must also keep the window hidden until AccessKit is initialized.
|
||||
.with_visible(false)
|
||||
.build(event_loop)
|
||||
.unwrap()
|
||||
Ok(
|
||||
epi_integration::window_builder(title, native_options, &window_settings)
|
||||
.build(event_loop)?,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn set_window(&mut self, window: winit::window::Window) {
|
||||
fn set_window(
|
||||
&mut self,
|
||||
window: winit::window::Window,
|
||||
) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||
self.window = Some(window);
|
||||
if let Some(running) = &mut self.running {
|
||||
unsafe {
|
||||
running.painter.set_window(self.window.as_ref()).unwrap();
|
||||
running.painter.set_window(self.window.as_ref())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
fn drop_window(&mut self) {
|
||||
fn drop_window(&mut self) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||
self.window = None;
|
||||
if let Some(running) = &mut self.running {
|
||||
unsafe {
|
||||
running.painter.set_window(None).unwrap();
|
||||
running.painter.set_window(None)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_run_state(
|
||||
@@ -962,7 +973,7 @@ mod wgpu_integration {
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
storage: Option<Box<dyn epi::Storage>>,
|
||||
window: winit::window::Window,
|
||||
) {
|
||||
) -> std::result::Result<(), wgpu::RequestDeviceError> {
|
||||
#[allow(unsafe_code, unused_mut, unused_unsafe)]
|
||||
let painter = unsafe {
|
||||
let mut painter = egui_wgpu::winit::Painter::new(
|
||||
@@ -970,7 +981,7 @@ mod wgpu_integration {
|
||||
self.native_options.multisampling.max(1) as _,
|
||||
self.native_options.depth_buffer,
|
||||
);
|
||||
painter.set_window(Some(&window)).unwrap();
|
||||
painter.set_window(Some(&window))?;
|
||||
painter
|
||||
};
|
||||
|
||||
@@ -1028,6 +1039,8 @@ mod wgpu_integration {
|
||||
app,
|
||||
});
|
||||
self.window = Some(window);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,8 +1148,8 @@ mod wgpu_integration {
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
) -> EventResult {
|
||||
match event {
|
||||
) -> Result<EventResult> {
|
||||
Ok(match event {
|
||||
winit::event::Event::Resumed => {
|
||||
if let Some(running) = &self.running {
|
||||
if self.window.is_none() {
|
||||
@@ -1145,8 +1158,8 @@ mod wgpu_integration {
|
||||
running.integration.frame.storage(),
|
||||
&self.app_name,
|
||||
&self.native_options,
|
||||
);
|
||||
self.set_window(window);
|
||||
)?;
|
||||
self.set_window(window)?;
|
||||
}
|
||||
} else {
|
||||
let storage = epi_integration::create_storage(&self.app_name);
|
||||
@@ -1155,14 +1168,14 @@ mod wgpu_integration {
|
||||
storage.as_deref(),
|
||||
&self.app_name,
|
||||
&self.native_options,
|
||||
);
|
||||
self.init_run_state(event_loop, storage, window);
|
||||
)?;
|
||||
self.init_run_state(event_loop, storage, window)?;
|
||||
}
|
||||
EventResult::RepaintNow
|
||||
}
|
||||
winit::event::Event::Suspended => {
|
||||
#[cfg(target_os = "android")]
|
||||
self.drop_window();
|
||||
self.drop_window()?;
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
@@ -1212,7 +1225,8 @@ mod wgpu_integration {
|
||||
winit::event::WindowEvent::CloseRequested
|
||||
if running.integration.should_close() =>
|
||||
{
|
||||
return EventResult::Exit
|
||||
tracing::debug!("Received WindowEvent::CloseRequested");
|
||||
return Ok(EventResult::Exit);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -1250,7 +1264,7 @@ mod wgpu_integration {
|
||||
}
|
||||
}
|
||||
_ => EventResult::Wait,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1258,7 +1272,7 @@ mod wgpu_integration {
|
||||
app_name: &str,
|
||||
mut native_options: epi::NativeOptions,
|
||||
app_creator: epi::AppCreator,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if native_options.run_and_return {
|
||||
with_event_loop(native_options, |event_loop, mut native_options| {
|
||||
if native_options.centered {
|
||||
@@ -1267,8 +1281,8 @@ mod wgpu_integration {
|
||||
|
||||
let wgpu_eframe =
|
||||
WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
|
||||
run_and_return(event_loop, wgpu_eframe);
|
||||
});
|
||||
run_and_return(event_loop, wgpu_eframe)
|
||||
})
|
||||
} else {
|
||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user