diff --git a/Cargo.lock b/Cargo.lock index 555ab110..9366a772 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,6 +1325,7 @@ dependencies = [ "raw-window-handle 0.5.0", "ron", "serde", + "thiserror", "tracing", "tts", "wasm-bindgen", diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index c5898f13..bcabaeb7 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -6,6 +6,9 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C ## Unreleased +#### Desktop/Native: +* `eframe::run_native` now returns a `Result` ([#2433](https://github.com/emilk/egui/pull/2433)). + ## 0.20.1 - 2022-12-11 * Fix docs.rs build ([#2420](https://github.com/emilk/egui/pull/2420)). diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 5298eb91..e4a25b90 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -72,6 +72,7 @@ egui = { version = "0.20.0", path = "../egui", default-features = false, feature "bytemuck", "tracing", ] } +thiserror = "1.0.37" tracing = { version = "0.1", default-features = false, features = ["std"] } #! ### Optional dependencies diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 7ebcb5c1..228a005d 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -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; } diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 65c8d571..28f2adb1 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -127,7 +127,7 @@ pub async fn start_web( canvas_id: &str, web_options: WebOptions, app_creator: AppCreator, -) -> Result { +) -> std::result::Result { 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 = std::result::Result; + // --------------------------------------------------------------------------- /// Profiling macro for feature "puffin" diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 0cda6fcb..b3c76660 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -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, ) -> 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 { #[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 diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index 29e86abf..331f40fe 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -26,6 +26,7 @@ impl FileStorage { /// Store the state in this .ron file. pub fn from_ron_filepath(ron_filepath: impl Into) -> 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, diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index cca2e418..a25ed4ed 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -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, event: &winit::event::Event<'_, UserEvent>, - ) -> EventResult; + ) -> Result; } 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( mut native_options: epi::NativeOptions, - f: impl FnOnce(&mut EventLoop, NativeOptions), -) { + f: impl FnOnce(&mut EventLoop, NativeOptions) -> R, +) -> R { use std::cell::RefCell; thread_local!(static EVENT_LOOP: RefCell>> = 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, mut winit_app: impl WinitApp) { +fn run_and_return( + event_loop: &mut EventLoop, + 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, 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, 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, mut winit_app: impl Win event_loop.run_return(|_, _, control_flow| { control_flow.set_exit(); }); + + returned_result } fn run_and_exit(event_loop: EventLoop, 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, 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, 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 { 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::::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) { 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, 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) { + fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget) -> 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, event: &winit::event::Event<'_, UserEvent>, - ) -> EventResult { - match event { + ) -> Result { + 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, storage: Option<&dyn epi::Storage>, - title: &String, + title: &str, native_options: &NativeOptions, - ) -> winit::window::Window { + ) -> Result { 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, storage: Option>, 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, event: &winit::event::Event<'_, UserEvent>, - ) -> EventResult { - match event { + ) -> Result { + 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(); diff --git a/crates/egui-wgpu/CHANGELOG.md b/crates/egui-wgpu/CHANGELOG.md index b534e606..6fc0bc28 100644 --- a/crates/egui-wgpu/CHANGELOG.md +++ b/crates/egui-wgpu/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file. ## Unreleased -* Fix panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)) +* Return `Err` instead of panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)). ## 0.20.0 - 2022-12-08 - web support diff --git a/crates/egui-winit/src/window_settings.rs b/crates/egui-winit/src/window_settings.rs index 104d9041..e7bd746c 100644 --- a/crates/egui-winit/src/window_settings.rs +++ b/crates/egui-winit/src/window_settings.rs @@ -46,12 +46,13 @@ impl WindowSettings { &self, mut window: winit::window::WindowBuilder, ) -> winit::window::WindowBuilder { - if !cfg!(target_os = "windows") { - // If the app last ran on two monitors and only one is now connected, then - // the given position is invalid. - // If this happens on Mac, the window is clamped into valid area. - // If this happens on Windows, the window is hidden and very difficult to find. - // So we don't restore window positions on Windows. + // If the app last ran on two monitors and only one is now connected, then + // the given position is invalid. + // If this happens on Mac, the window is clamped into valid area. + // If this happens on Windows, the window is hidden and very difficult to find. + // So we don't restore window positions on Windows. + let try_restore_position = !cfg!(target_os = "windows"); + if try_restore_position { if let Some(pos) = self.position { window = window.with_position(winit::dpi::PhysicalPosition { x: pos.x as f64, @@ -74,4 +75,12 @@ impl WindowSettings { window } } + + pub fn clamp_to_sane_values(&mut self) { + if let Some(size) = &mut self.inner_size_points { + // Prevent ridiculously small windows + let min_size = egui::Vec2::splat(64.0); + *size = size.max(min_size); + } + } } diff --git a/crates/egui_demo_app/src/main.rs b/crates/egui_demo_app/src/main.rs index a697e3a8..d57e9d64 100644 --- a/crates/egui_demo_app/src/main.rs +++ b/crates/egui_demo_app/src/main.rs @@ -3,7 +3,18 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release // When compiling natively: -fn main() { +fn main() -> Result<(), eframe::EframeError> { + { + // Silence wgpu log spam (https://github.com/gfx-rs/wgpu/issues/3206) + let mut rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned()); + for loud_crate in ["naga", "wgpu_core", "wgpu_hal"] { + if !rust_log.contains(&format!("{loud_crate}=")) { + rust_log += &format!(",{loud_crate}=warn"); + } + } + std::env::set_var("RUST_LOG", rust_log); + } + // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt::init(); @@ -21,5 +32,5 @@ fn main() { "egui demo app", options, Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))), - ); + ) } diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 15f6dc2e..3c20a7c1 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -223,9 +223,9 @@ fn create_display( height: 600.0, }) .with_title("egui_glow example") - .with_visible(false) + .with_visible(false) // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 .build(event_loop) - .unwrap(); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 + .unwrap(); // a lot of the code below has been lifted from glutin example in their repo. let glutin_window_context = unsafe { GlutinWindowContext::new(winit_window) }; diff --git a/examples/confirm_exit/src/main.rs b/examples/confirm_exit/src/main.rs index f909ede5..167d4cbc 100644 --- a/examples/confirm_exit/src/main.rs +++ b/examples/confirm_exit/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(320.0, 240.0)), ..Default::default() @@ -11,7 +11,7 @@ fn main() { "Confirm exit", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/custom_3d_glow/src/main.rs b/examples/custom_3d_glow/src/main.rs index f3f0c0e6..b7a828c5 100644 --- a/examples/custom_3d_glow/src/main.rs +++ b/examples/custom_3d_glow/src/main.rs @@ -6,7 +6,7 @@ use eframe::egui; use egui::mutex::Mutex; use std::sync::Arc; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(350.0, 380.0)), multisampling: 8, @@ -17,7 +17,7 @@ fn main() { "Custom 3D painting in eframe using glow", options, Box::new(|cc| Box::new(MyApp::new(cc))), - ); + ) } struct MyApp { diff --git a/examples/custom_3d_three-d/src/main.rs b/examples/custom_3d_three-d/src/main.rs index cd310cfa..d9381cdb 100644 --- a/examples/custom_3d_three-d/src/main.rs +++ b/examples/custom_3d_three-d/src/main.rs @@ -4,7 +4,7 @@ use eframe::egui; #[cfg(not(target_arch = "wasm32"))] -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(550.0, 610.0)), multisampling: 8, @@ -16,7 +16,7 @@ fn main() { "Custom 3D painting in eframe!", options, Box::new(|cc| Box::new(MyApp::new(cc))), - ); + ) } pub struct MyApp { diff --git a/examples/custom_font/src/main.rs b/examples/custom_font/src/main.rs index 0b763d13..3f65b422 100644 --- a/examples/custom_font/src/main.rs +++ b/examples/custom_font/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(320.0, 240.0)), ..Default::default() @@ -11,7 +11,7 @@ fn main() { "egui example: custom font", options, Box::new(|cc| Box::new(MyApp::new(cc))), - ); + ) } fn setup_custom_fonts(ctx: &egui::Context) { diff --git a/examples/custom_font_style/src/main.rs b/examples/custom_font_style/src/main.rs index 43c482e8..c6c57d10 100644 --- a/examples/custom_font_style/src/main.rs +++ b/examples/custom_font_style/src/main.rs @@ -58,14 +58,14 @@ impl eframe::App for MyApp { } } -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions::default(); eframe::run_native( "egui example: global font style", options, Box::new(|cc| Box::new(MyApp::new(cc))), - ); + ) } pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index 10301cc3..b41dae7a 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -4,7 +4,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { // Hide the OS-specific "chrome" around the window: decorated: false, @@ -18,7 +18,7 @@ fn main() { "Custom window frame", // unused title options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/download_image/src/main.rs b/examples/download_image/src/main.rs index 4922956c..4eaf6e95 100644 --- a/examples/download_image/src/main.rs +++ b/examples/download_image/src/main.rs @@ -4,13 +4,13 @@ use eframe::egui; use egui_extras::RetainedImage; use poll_promise::Promise; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions::default(); eframe::run_native( "Download and show an image with eframe/egui", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/file_dialog/src/main.rs b/examples/file_dialog/src/main.rs index 501cae24..e26d9861 100644 --- a/examples/file_dialog/src/main.rs +++ b/examples/file_dialog/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { drag_and_drop_support: true, initial_window_size: Some(egui::vec2(320.0, 240.0)), @@ -12,7 +12,7 @@ fn main() { "Native file dialogs and drag-and-drop files", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index ec945bae..dcef5edf 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt::init(); @@ -14,7 +14,7 @@ fn main() { "My egui App", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } struct MyApp { diff --git a/examples/keyboard_events/src/main.rs b/examples/keyboard_events/src/main.rs index ab1e8ec8..1045fc05 100644 --- a/examples/keyboard_events/src/main.rs +++ b/examples/keyboard_events/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; use egui::*; -fn main() { +fn main() -> Result<(), eframe::EframeError> { // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt::init(); @@ -11,7 +11,7 @@ fn main() { "Keyboard events", options, Box::new(|_cc| Box::new(Content::default())), - ); + ) } #[derive(Default)] diff --git a/examples/puffin_profiler/src/main.rs b/examples/puffin_profiler/src/main.rs index 26f7252a..1bc6b2f9 100644 --- a/examples/puffin_profiler/src/main.rs +++ b/examples/puffin_profiler/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { start_puffin_server(); // NOTE: you may only want to call this if the users specifies some flag or clicks a button! let options = eframe::NativeOptions::default(); @@ -10,7 +10,7 @@ fn main() { "My egui App", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/retained_image/src/main.rs b/examples/retained_image/src/main.rs index dc6b59c0..790d842c 100644 --- a/examples/retained_image/src/main.rs +++ b/examples/retained_image/src/main.rs @@ -3,7 +3,7 @@ use eframe::egui; use egui_extras::RetainedImage; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(300.0, 900.0)), ..Default::default() @@ -13,7 +13,7 @@ fn main() { "Show an image with eframe/egui", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } struct MyApp { diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 4019430f..4ee427b2 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -6,13 +6,13 @@ use eframe::{ }; use itertools::Itertools as _; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions::default(); eframe::run_native( "Take screenshots and display with eframe/egui", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/serial_windows/src/main.rs b/examples/serial_windows/src/main.rs index 13544319..5e223d18 100644 --- a/examples/serial_windows/src/main.rs +++ b/examples/serial_windows/src/main.rs @@ -2,7 +2,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { if cfg!(target_os = "macos") { eprintln!("WARNING: this example does not work on Mac! See https://github.com/emilk/egui/issues/1918"); } @@ -18,7 +18,7 @@ fn main() { "First Window", options.clone(), Box::new(|_cc| Box::new(MyApp::default())), - ); + )?; std::thread::sleep(std::time::Duration::from_secs(2)); @@ -27,7 +27,7 @@ fn main() { "Second Window", options.clone(), Box::new(|_cc| Box::new(MyApp::default())), - ); + )?; std::thread::sleep(std::time::Duration::from_secs(2)); @@ -36,7 +36,7 @@ fn main() { "Third Window", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } #[derive(Default)] diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 63baa552..7640f2cc 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -6,7 +6,7 @@ use eframe::egui; -fn main() { +fn main() -> Result<(), eframe::EframeError> { let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(1000.0, 700.0)), ..Default::default() @@ -15,7 +15,7 @@ fn main() { "svg example", options, Box::new(|_cc| Box::new(MyApp::default())), - ); + ) } struct MyApp {