eframe/
epi.rs

1//! Platform-agnostic interface for writing apps using [`egui`] (epi = egui programming interface).
2//!
3//! `epi` provides interfaces for window management and serialization.
4//!
5//! Start by looking at the [`App`] trait, and implement [`App::update`].
6
7#![warn(missing_docs)] // Let's keep `epi` well-documented.
8
9#[cfg(target_arch = "wasm32")]
10use std::any::Any;
11
12#[cfg(not(target_arch = "wasm32"))]
13#[cfg(any(feature = "glow", feature = "wgpu"))]
14pub use crate::native::winit_integration::UserEvent;
15
16#[cfg(not(target_arch = "wasm32"))]
17use raw_window_handle::{
18    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
19    RawWindowHandle, WindowHandle,
20};
21#[cfg(not(target_arch = "wasm32"))]
22use static_assertions::assert_not_impl_any;
23
24#[cfg(not(target_arch = "wasm32"))]
25#[cfg(any(feature = "glow", feature = "wgpu"))]
26pub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes};
27
28/// Hook into the building of an event loop before it is run
29///
30/// You can configure any platform specific details required on top of the default configuration
31/// done by `EFrame`.
32#[cfg(not(target_arch = "wasm32"))]
33#[cfg(any(feature = "glow", feature = "wgpu"))]
34pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;
35
36/// Hook into the building of a the native window.
37///
38/// You can configure any platform specific details required on top of the default configuration
39/// done by `eframe`.
40#[cfg(not(target_arch = "wasm32"))]
41#[cfg(any(feature = "glow", feature = "wgpu"))]
42pub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>;
43
44type DynError = Box<dyn std::error::Error + Send + Sync>;
45
46/// This is how your app is created.
47///
48/// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.
49pub type AppCreator<'app> =
50    Box<dyn 'app + FnOnce(&CreationContext<'_>) -> Result<Box<dyn 'app + App>, DynError>>;
51
52/// Data that is passed to [`AppCreator`] that can be used to setup and initialize your app.
53pub struct CreationContext<'s> {
54    /// The egui Context.
55    ///
56    /// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],
57    /// [`egui::Context::set_visuals_of`] etc.
58    pub egui_ctx: egui::Context,
59
60    /// Information about the surrounding environment.
61    pub integration_info: IntegrationInfo,
62
63    /// You can use the storage to restore app state(requires the "persistence" feature).
64    pub storage: Option<&'s dyn Storage>,
65
66    /// The [`glow::Context`] allows you to initialize OpenGL resources (e.g. shaders) that
67    /// you might want to use later from a [`egui::PaintCallback`].
68    ///
69    /// Only available when compiling with the `glow` feature and using [`Renderer::Glow`].
70    #[cfg(feature = "glow")]
71    pub gl: Option<std::sync::Arc<glow::Context>>,
72
73    /// The `get_proc_address` wrapper of underlying GL context
74    #[cfg(feature = "glow")]
75    pub get_proc_address: Option<&'s dyn Fn(&std::ffi::CStr) -> *const std::ffi::c_void>,
76
77    /// The underlying WGPU render state.
78    ///
79    /// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
80    ///
81    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
82    #[cfg(feature = "wgpu")]
83    pub wgpu_render_state: Option<egui_wgpu::RenderState>,
84
85    /// Raw platform window handle
86    #[cfg(not(target_arch = "wasm32"))]
87    pub(crate) raw_window_handle: Result<RawWindowHandle, HandleError>,
88
89    /// Raw platform display handle for window
90    #[cfg(not(target_arch = "wasm32"))]
91    pub(crate) raw_display_handle: Result<RawDisplayHandle, HandleError>,
92}
93
94#[allow(unsafe_code)]
95#[cfg(not(target_arch = "wasm32"))]
96impl HasWindowHandle for CreationContext<'_> {
97    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
98        // Safety: the lifetime is correct.
99        unsafe { Ok(WindowHandle::borrow_raw(self.raw_window_handle.clone()?)) }
100    }
101}
102
103#[allow(unsafe_code)]
104#[cfg(not(target_arch = "wasm32"))]
105impl HasDisplayHandle for CreationContext<'_> {
106    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
107        // Safety: the lifetime is correct.
108        unsafe { Ok(DisplayHandle::borrow_raw(self.raw_display_handle.clone()?)) }
109    }
110}
111
112// ----------------------------------------------------------------------------
113
114/// Implement this trait to write apps that can be compiled for both web/wasm and desktop/native using [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe).
115pub trait App {
116    /// Called each time the UI needs repainting, which may be many times per second.
117    ///
118    /// Put your widgets into a [`egui::SidePanel`], [`egui::TopBottomPanel`], [`egui::CentralPanel`], [`egui::Window`] or [`egui::Area`].
119    ///
120    /// The [`egui::Context`] can be cloned and saved if you like.
121    ///
122    /// To force a repaint, call [`egui::Context::request_repaint`] at any time (e.g. from another thread).
123    ///
124    /// This is called for the root viewport ([`egui::ViewportId::ROOT`]).
125    /// Use [`egui::Context::show_viewport_deferred`] to spawn additional viewports (windows).
126    /// (A "viewport" in egui means an native OS window).
127    fn update(&mut self, ctx: &egui::Context, frame: &mut Frame);
128
129    /// Get a handle to the app.
130    ///
131    /// Can be used from web to interact or other external context.
132    ///
133    /// You need to implement this if you want to be able to access the application from JS using [`crate::WebRunner::app_mut`].
134    ///
135    /// This is needed because downcasting `Box<dyn App>` -> `Box<dyn Any>` to get &`ConcreteApp` is not simple in current rust.
136    ///
137    /// Just copy-paste this as your implementation:
138    /// ```ignore
139    /// #[cfg(target_arch = "wasm32")]
140    /// fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
141    ///     Some(&mut *self)
142    /// }
143    /// ```
144    #[cfg(target_arch = "wasm32")]
145    fn as_any_mut(&mut self) -> Option<&mut dyn Any> {
146        None
147    }
148
149    /// Called on shutdown, and perhaps at regular intervals. Allows you to save state.
150    ///
151    /// Only called when the "persistence" feature is enabled.
152    ///
153    /// On web the state is stored to "Local Storage".
154    ///
155    /// On native the path is picked using [`crate::storage_dir`].
156    /// The path can be customized via [`NativeOptions::persistence_path`].
157    fn save(&mut self, _storage: &mut dyn Storage) {}
158
159    /// Called once on shutdown, after [`Self::save`].
160    ///
161    /// If you need to abort an exit check `ctx.input(|i| i.viewport().close_requested())`
162    /// and respond with [`egui::ViewportCommand::CancelClose`].
163    ///
164    /// To get a [`glow`] context you need to compile with the `glow` feature flag,
165    /// and run eframe with the glow backend.
166    #[cfg(feature = "glow")]
167    fn on_exit(&mut self, _gl: Option<&glow::Context>) {}
168
169    /// Called once on shutdown, after [`Self::save`].
170    ///
171    /// If you need to abort an exit use [`Self::on_close_event`].
172    #[cfg(not(feature = "glow"))]
173    fn on_exit(&mut self) {}
174
175    // ---------
176    // Settings:
177
178    /// Time between automatic calls to [`Self::save`]
179    fn auto_save_interval(&self) -> std::time::Duration {
180        std::time::Duration::from_secs(30)
181    }
182
183    /// Background color values for the app, e.g. what is sent to `gl.clearColor`.
184    ///
185    /// This is the background of your windows if you don't set a central panel.
186    ///
187    /// ATTENTION:
188    /// Since these float values go to the render as-is, any color space conversion as done
189    /// e.g. by converting from [`egui::Color32`] to [`egui::Rgba`] may cause incorrect results.
190    /// egui recommends that rendering backends use a normal "gamma-space" (non-sRGB-aware) blending,
191    ///  which means the values you return here should also be in `sRGB` gamma-space in the 0-1 range.
192    /// You can use [`egui::Color32::to_normalized_gamma_f32`] for this.
193    fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] {
194        // NOTE: a bright gray makes the shadows of the windows look weird.
195        // We use a bit of transparency so that if the user switches on the
196        // `transparent()` option they get immediate results.
197        egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).to_normalized_gamma_f32()
198
199        // _visuals.window_fill() would also be a natural choice
200    }
201
202    /// Controls whether or not the egui memory (window positions etc) will be
203    /// persisted (only if the "persistence" feature is enabled).
204    fn persist_egui_memory(&self) -> bool {
205        true
206    }
207
208    /// A hook for manipulating or filtering raw input before it is processed by [`Self::update`].
209    ///
210    /// This function provides a way to modify or filter input events before they are processed by egui.
211    ///
212    /// It can be used to prevent specific keyboard shortcuts or mouse events from being processed by egui.
213    ///
214    /// Additionally, it can be used to inject custom keyboard or mouse events into the input stream, which can be useful for implementing features like a virtual keyboard.
215    ///
216    /// # Arguments
217    ///
218    /// * `_ctx` - The context of the egui, which provides access to the current state of the egui.
219    /// * `_raw_input` - The raw input events that are about to be processed. This can be modified to change the input that egui processes.
220    ///
221    /// # Note
222    ///
223    /// This function does not return a value. Any changes to the input should be made directly to `_raw_input`.
224    fn raw_input_hook(&mut self, _ctx: &egui::Context, _raw_input: &mut egui::RawInput) {}
225}
226
227/// Selects the level of hardware graphics acceleration.
228#[cfg(not(target_arch = "wasm32"))]
229#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
230pub enum HardwareAcceleration {
231    /// Require graphics acceleration.
232    Required,
233
234    /// Prefer graphics acceleration, but fall back to software.
235    Preferred,
236
237    /// Do NOT use graphics acceleration.
238    ///
239    /// On some platforms (macOS) this is ignored and treated the same as [`Self::Preferred`].
240    Off,
241}
242
243/// Options controlling the behavior of a native window.
244///
245/// Additional windows can be opened using (egui viewports)[`egui::viewport`].
246///
247/// Set the window title and size using [`Self::viewport`].
248///
249/// ### Application id
250/// [`egui::ViewportBuilder::with_app_id`] is used for determining the folder to persist the app to.
251///
252/// On native the path is picked using [`crate::storage_dir`].
253///
254/// If you don't set an app id, the title argument to [`crate::run_native`]
255/// will be used as app id instead.
256#[cfg(not(target_arch = "wasm32"))]
257pub struct NativeOptions {
258    /// Controls the native window of the root viewport.
259    ///
260    /// This is where you set things like window title and size.
261    ///
262    /// If you don't set an icon, a default egui icon will be used.
263    /// To avoid this, set the icon to [`egui::IconData::default`].
264    pub viewport: egui::ViewportBuilder,
265
266    /// Turn on vertical syncing, limiting the FPS to the display refresh rate.
267    ///
268    /// The default is `true`.
269    pub vsync: bool,
270
271    /// Set the level of the multisampling anti-aliasing (MSAA).
272    ///
273    /// Must be a power-of-two. Higher = more smooth 3D.
274    ///
275    /// A value of `0` turns it off (default).
276    ///
277    /// `egui` already performs anti-aliasing via "feathering"
278    /// (controlled by [`egui::epaint::TessellationOptions`]),
279    /// but if you are embedding 3D in egui you may want to turn on multisampling.
280    pub multisampling: u16,
281
282    /// Sets the number of bits in the depth buffer.
283    ///
284    /// `egui` doesn't need the depth buffer, so the default value is 0.
285    pub depth_buffer: u8,
286
287    /// Sets the number of bits in the stencil buffer.
288    ///
289    /// `egui` doesn't need the stencil buffer, so the default value is 0.
290    pub stencil_buffer: u8,
291
292    /// Specify whether or not hardware acceleration is preferred, required, or not.
293    ///
294    /// Default: [`HardwareAcceleration::Preferred`].
295    pub hardware_acceleration: HardwareAcceleration,
296
297    /// What rendering backend to use.
298    #[cfg(any(feature = "glow", feature = "wgpu"))]
299    pub renderer: Renderer,
300
301    /// This controls what happens when you close the main eframe window.
302    ///
303    /// If `true`, execution will continue after the eframe window is closed.
304    /// If `false`, the app will close once the eframe window is closed.
305    ///
306    /// This is `true` by default, and the `false` option is only there
307    /// so we can revert if we find any bugs.
308    ///
309    /// This feature was introduced in <https://github.com/emilk/egui/pull/1889>.
310    ///
311    /// When `true`, [`winit::platform::run_on_demand::EventLoopExtRunOnDemand`] is used.
312    /// When `false`, [`winit::event_loop::EventLoop::run`] is used.
313    pub run_and_return: bool,
314
315    /// Hook into the building of an event loop before it is run.
316    ///
317    /// Specify a callback here in case you need to make platform specific changes to the
318    /// event loop before it is run.
319    ///
320    /// Note: A [`NativeOptions`] clone will not include any `event_loop_builder` hook.
321    #[cfg(any(feature = "glow", feature = "wgpu"))]
322    pub event_loop_builder: Option<EventLoopBuilderHook>,
323
324    /// Hook into the building of a window.
325    ///
326    /// Specify a callback here in case you need to make platform specific changes to the
327    /// window appearance.
328    ///
329    /// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.
330    #[cfg(any(feature = "glow", feature = "wgpu"))]
331    pub window_builder: Option<WindowBuilderHook>,
332
333    #[cfg(feature = "glow")]
334    /// Needed for cross compiling for VirtualBox VMSVGA driver with OpenGL ES 2.0 and OpenGL 2.1 which doesn't support SRGB texture.
335    /// See <https://github.com/emilk/egui/pull/1993>.
336    ///
337    /// For OpenGL ES 2.0: set this to [`egui_glow::ShaderVersion::Es100`] to solve blank texture problem (by using the "fallback shader").
338    pub shader_version: Option<egui_glow::ShaderVersion>,
339
340    /// On desktop: make the window position to be centered at initialization.
341    ///
342    /// Platform specific:
343    ///
344    /// Wayland desktop currently not supported.
345    pub centered: bool,
346
347    /// Configures wgpu instance/device/adapter/surface creation and renderloop.
348    #[cfg(feature = "wgpu")]
349    pub wgpu_options: egui_wgpu::WgpuConfiguration,
350
351    /// Controls whether or not the native window position and size will be
352    /// persisted (only if the "persistence" feature is enabled).
353    pub persist_window: bool,
354
355    /// The folder where `eframe` will store the app state. If not set, eframe will use a default
356    /// data storage path for each target system.
357    pub persistence_path: Option<std::path::PathBuf>,
358
359    /// Controls whether to apply dithering to minimize banding artifacts.
360    ///
361    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between
362    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in "gamma space".
363    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.
364    ///
365    /// Defaults to true.
366    pub dithering: bool,
367
368    /// Android application for `winit`'s event loop.
369    ///
370    /// This value is required on Android to correctly create the event loop. See
371    /// [`EventLoopBuilder::build`] and [`with_android_app`] for details.
372    ///
373    /// [`EventLoopBuilder::build`]: winit::event_loop::EventLoopBuilder::build
374    /// [`with_android_app`]: winit::platform::android::EventLoopBuilderExtAndroid::with_android_app
375    #[cfg(target_os = "android")]
376    pub android_app: Option<winit::platform::android::activity::AndroidApp>,
377}
378
379#[cfg(not(target_arch = "wasm32"))]
380impl Clone for NativeOptions {
381    fn clone(&self) -> Self {
382        Self {
383            viewport: self.viewport.clone(),
384
385            #[cfg(any(feature = "glow", feature = "wgpu"))]
386            event_loop_builder: None, // Skip any builder callbacks if cloning
387
388            #[cfg(any(feature = "glow", feature = "wgpu"))]
389            window_builder: None, // Skip any builder callbacks if cloning
390
391            #[cfg(feature = "wgpu")]
392            wgpu_options: self.wgpu_options.clone(),
393
394            persistence_path: self.persistence_path.clone(),
395
396            #[cfg(target_os = "android")]
397            android_app: self.android_app.clone(),
398
399            ..*self
400        }
401    }
402}
403
404#[cfg(not(target_arch = "wasm32"))]
405impl Default for NativeOptions {
406    fn default() -> Self {
407        Self {
408            viewport: Default::default(),
409
410            vsync: true,
411            multisampling: 0,
412            depth_buffer: 0,
413            stencil_buffer: 0,
414            hardware_acceleration: HardwareAcceleration::Preferred,
415
416            #[cfg(any(feature = "glow", feature = "wgpu"))]
417            renderer: Renderer::default(),
418
419            run_and_return: true,
420
421            #[cfg(any(feature = "glow", feature = "wgpu"))]
422            event_loop_builder: None,
423
424            #[cfg(any(feature = "glow", feature = "wgpu"))]
425            window_builder: None,
426
427            #[cfg(feature = "glow")]
428            shader_version: None,
429
430            centered: false,
431
432            #[cfg(feature = "wgpu")]
433            wgpu_options: egui_wgpu::WgpuConfiguration::default(),
434
435            persist_window: true,
436
437            persistence_path: None,
438
439            dithering: true,
440
441            #[cfg(target_os = "android")]
442            android_app: None,
443        }
444    }
445}
446
447// ----------------------------------------------------------------------------
448
449/// Options when using `eframe` in a web page.
450#[cfg(target_arch = "wasm32")]
451pub struct WebOptions {
452    /// Sets the number of bits in the depth buffer.
453    ///
454    /// `egui` doesn't need the depth buffer, so the default value is 0.
455    /// Unused by webgl context as of writing.
456    pub depth_buffer: u8,
457
458    /// Which version of WebGL context to select
459    ///
460    /// Default: [`WebGlContextOption::BestFirst`].
461    #[cfg(feature = "glow")]
462    pub webgl_context_option: WebGlContextOption,
463
464    /// Configures wgpu instance/device/adapter/surface creation and renderloop.
465    #[cfg(feature = "wgpu")]
466    pub wgpu_options: egui_wgpu::WgpuConfiguration,
467
468    /// Controls whether to apply dithering to minimize banding artifacts.
469    ///
470    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between
471    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in "gamma space".
472    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.
473    ///
474    /// Defaults to true.
475    pub dithering: bool,
476
477    /// If the web event corresponding to an egui event should be propagated
478    /// to the rest of the web page.
479    ///
480    /// The default is `false`, meaning
481    /// [`stopPropagation`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)
482    /// is called on every event.
483    pub should_propagate_event: Box<dyn Fn(&egui::Event) -> bool>,
484}
485
486#[cfg(target_arch = "wasm32")]
487impl Default for WebOptions {
488    fn default() -> Self {
489        Self {
490            depth_buffer: 0,
491
492            #[cfg(feature = "glow")]
493            webgl_context_option: WebGlContextOption::BestFirst,
494
495            #[cfg(feature = "wgpu")]
496            wgpu_options: egui_wgpu::WgpuConfiguration::default(),
497
498            dithering: true,
499
500            should_propagate_event: Box::new(|_| false),
501        }
502    }
503}
504
505// ----------------------------------------------------------------------------
506
507/// WebGL Context options
508#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
509#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
510pub enum WebGlContextOption {
511    /// Force Use WebGL1.
512    WebGl1,
513
514    /// Force use WebGL2.
515    WebGl2,
516
517    /// Use WebGL2 first.
518    BestFirst,
519
520    /// Use WebGL1 first
521    CompatibilityFirst,
522}
523
524// ----------------------------------------------------------------------------
525
526/// What rendering backend to use.
527///
528/// You need to enable the "glow" and "wgpu" features to have a choice.
529#[cfg(any(feature = "glow", feature = "wgpu"))]
530#[derive(Clone, Copy, Debug, PartialEq, Eq)]
531#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
532#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
533pub enum Renderer {
534    /// Use [`egui_glow`] renderer for [`glow`](https://github.com/grovesNL/glow).
535    #[cfg(feature = "glow")]
536    Glow,
537
538    /// Use [`egui_wgpu`] renderer for [`wgpu`](https://github.com/gfx-rs/wgpu).
539    #[cfg(feature = "wgpu")]
540    Wgpu,
541}
542
543#[cfg(any(feature = "glow", feature = "wgpu"))]
544impl Default for Renderer {
545    fn default() -> Self {
546        #[cfg(not(feature = "glow"))]
547        #[cfg(not(feature = "wgpu"))]
548        compile_error!("eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'");
549
550        #[cfg(feature = "glow")]
551        #[cfg(not(feature = "wgpu"))]
552        return Self::Glow;
553
554        #[cfg(not(feature = "glow"))]
555        #[cfg(feature = "wgpu")]
556        return Self::Wgpu;
557
558        // By default, only the `glow` feature is enabled, so if the user added `wgpu` to the feature list
559        // they probably wanted to use wgpu:
560        #[cfg(feature = "glow")]
561        #[cfg(feature = "wgpu")]
562        return Self::Wgpu;
563    }
564}
565
566#[cfg(any(feature = "glow", feature = "wgpu"))]
567impl std::fmt::Display for Renderer {
568    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569        match self {
570            #[cfg(feature = "glow")]
571            Self::Glow => "glow".fmt(f),
572
573            #[cfg(feature = "wgpu")]
574            Self::Wgpu => "wgpu".fmt(f),
575        }
576    }
577}
578
579#[cfg(any(feature = "glow", feature = "wgpu"))]
580impl std::str::FromStr for Renderer {
581    type Err = String;
582
583    fn from_str(name: &str) -> Result<Self, String> {
584        match name.to_lowercase().as_str() {
585            #[cfg(feature = "glow")]
586            "glow" => Ok(Self::Glow),
587
588            #[cfg(feature = "wgpu")]
589            "wgpu" => Ok(Self::Wgpu),
590
591            _ => Err(format!("eframe renderer {name:?} is not available. Make sure that the corresponding eframe feature is enabled."))
592        }
593    }
594}
595
596// ----------------------------------------------------------------------------
597
598/// Represents the surroundings of your app.
599///
600/// It provides methods to inspect the surroundings (are we on the web?),
601/// access to persistent storage, and access to the rendering backend.
602pub struct Frame {
603    /// Information about the integration.
604    pub(crate) info: IntegrationInfo,
605
606    /// A place where you can store custom data in a way that persists when you restart the app.
607    pub(crate) storage: Option<Box<dyn Storage>>,
608
609    /// A reference to the underlying [`glow`] (OpenGL) context.
610    #[cfg(feature = "glow")]
611    pub(crate) gl: Option<std::sync::Arc<glow::Context>>,
612
613    /// Used to convert user custom [`glow::Texture`] to [`egui::TextureId`]
614    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
615    pub(crate) glow_register_native_texture:
616        Option<Box<dyn FnMut(glow::Texture) -> egui::TextureId>>,
617
618    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
619    #[cfg(feature = "wgpu")]
620    pub(crate) wgpu_render_state: Option<egui_wgpu::RenderState>,
621
622    /// Raw platform window handle
623    #[cfg(not(target_arch = "wasm32"))]
624    pub(crate) raw_window_handle: Result<RawWindowHandle, HandleError>,
625
626    /// Raw platform display handle for window
627    #[cfg(not(target_arch = "wasm32"))]
628    pub(crate) raw_display_handle: Result<RawDisplayHandle, HandleError>,
629}
630
631// Implementing `Clone` would violate the guarantees of `HasWindowHandle` and `HasDisplayHandle`.
632#[cfg(not(target_arch = "wasm32"))]
633assert_not_impl_any!(Frame: Clone);
634
635#[allow(unsafe_code)]
636#[cfg(not(target_arch = "wasm32"))]
637impl HasWindowHandle for Frame {
638    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
639        // Safety: the lifetime is correct.
640        unsafe { Ok(WindowHandle::borrow_raw(self.raw_window_handle.clone()?)) }
641    }
642}
643
644#[allow(unsafe_code)]
645#[cfg(not(target_arch = "wasm32"))]
646impl HasDisplayHandle for Frame {
647    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
648        // Safety: the lifetime is correct.
649        unsafe { Ok(DisplayHandle::borrow_raw(self.raw_display_handle.clone()?)) }
650    }
651}
652
653impl Frame {
654    /// True if you are in a web environment.
655    ///
656    /// Equivalent to `cfg!(target_arch = "wasm32")`
657    #[allow(clippy::unused_self)]
658    pub fn is_web(&self) -> bool {
659        cfg!(target_arch = "wasm32")
660    }
661
662    /// Information about the integration.
663    pub fn info(&self) -> &IntegrationInfo {
664        &self.info
665    }
666
667    /// A place where you can store custom data in a way that persists when you restart the app.
668    pub fn storage(&self) -> Option<&dyn Storage> {
669        self.storage.as_deref()
670    }
671
672    /// A place where you can store custom data in a way that persists when you restart the app.
673    pub fn storage_mut(&mut self) -> Option<&mut (dyn Storage + 'static)> {
674        self.storage.as_deref_mut()
675    }
676
677    /// A reference to the underlying [`glow`] (OpenGL) context.
678    ///
679    /// This can be used, for instance, to:
680    /// * Render things to offscreen buffers.
681    /// * Read the pixel buffer from the previous frame (`glow::Context::read_pixels`).
682    /// * Render things behind the egui windows.
683    ///
684    /// Note that all egui painting is deferred to after the call to [`App::update`]
685    /// ([`egui`] only collects [`egui::Shape`]s and then eframe paints them all in one go later on).
686    ///
687    /// To get a [`glow`] context you need to compile with the `glow` feature flag,
688    /// and run eframe using [`Renderer::Glow`].
689    #[cfg(feature = "glow")]
690    pub fn gl(&self) -> Option<&std::sync::Arc<glow::Context>> {
691        self.gl.as_ref()
692    }
693
694    /// Register your own [`glow::Texture`],
695    /// and then you can use the returned [`egui::TextureId`] to render your texture with [`egui`].
696    ///
697    /// This function will take the ownership of your [`glow::Texture`], so please do not delete your [`glow::Texture`] after registering.
698    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
699    pub fn register_native_glow_texture(&mut self, native: glow::Texture) -> egui::TextureId {
700        self.glow_register_native_texture.as_mut().unwrap()(native)
701    }
702
703    /// The underlying WGPU render state.
704    ///
705    /// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
706    ///
707    /// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
708    #[cfg(feature = "wgpu")]
709    pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {
710        self.wgpu_render_state.as_ref()
711    }
712}
713
714/// Information about the web environment (if applicable).
715#[derive(Clone, Debug)]
716#[cfg(target_arch = "wasm32")]
717pub struct WebInfo {
718    /// The browser user agent.
719    pub user_agent: String,
720
721    /// Information about the URL.
722    pub location: Location,
723}
724
725/// Information about the URL.
726///
727/// Everything has been percent decoded (`%20` -> ` ` etc).
728#[cfg(target_arch = "wasm32")]
729#[derive(Clone, Debug)]
730pub struct Location {
731    /// The full URL (`location.href`) without the hash, percent-decoded.
732    ///
733    /// Example: `"http://www.example.com:80/index.html?foo=bar"`.
734    pub url: String,
735
736    /// `location.protocol`
737    ///
738    /// Example: `"http:"`.
739    pub protocol: String,
740
741    /// `location.host`
742    ///
743    /// Example: `"example.com:80"`.
744    pub host: String,
745
746    /// `location.hostname`
747    ///
748    /// Example: `"example.com"`.
749    pub hostname: String,
750
751    /// `location.port`
752    ///
753    /// Example: `"80"`.
754    pub port: String,
755
756    /// The "#fragment" part of "www.example.com/index.html?query#fragment".
757    ///
758    /// Note that the leading `#` is included in the string.
759    /// Also known as "hash-link" or "anchor".
760    pub hash: String,
761
762    /// The "query" part of "www.example.com/index.html?query#fragment".
763    ///
764    /// Note that the leading `?` is NOT included in the string.
765    ///
766    /// Use [`Self::query_map`] to get the parsed version of it.
767    pub query: String,
768
769    /// The parsed "query" part of "www.example.com/index.html?query#fragment".
770    ///
771    /// "foo=hello&bar%20&foo=world" is parsed as `{"bar ": [""], "foo": ["hello", "world"]}`
772    pub query_map: std::collections::BTreeMap<String, Vec<String>>,
773
774    /// `location.origin`
775    ///
776    /// Example: `"http://www.example.com:80"`.
777    pub origin: String,
778}
779
780/// Information about the integration passed to the use app each frame.
781#[derive(Clone, Debug)]
782pub struct IntegrationInfo {
783    /// Information about the surrounding web environment.
784    #[cfg(target_arch = "wasm32")]
785    pub web_info: WebInfo,
786
787    /// Seconds of cpu usage (in seconds) on the previous frame.
788    ///
789    /// This includes [`App::update`] as well as rendering (except for vsync waiting).
790    ///
791    /// For a more detailed view of cpu usage, connect your preferred profiler by enabling it's feature in [`profiling`](https://crates.io/crates/profiling).
792    ///
793    /// `None` if this is the first frame.
794    pub cpu_usage: Option<f32>,
795}
796
797// ----------------------------------------------------------------------------
798
799/// A place where you can store custom data in a way that persists when you restart the app.
800///
801/// On the web this is backed by [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).
802/// On desktop this is backed by the file system.
803///
804/// See [`CreationContext::storage`] and [`App::save`].
805pub trait Storage {
806    /// Get the value for the given key.
807    fn get_string(&self, key: &str) -> Option<String>;
808
809    /// Set the value for the given key.
810    fn set_string(&mut self, key: &str, value: String);
811
812    /// write-to-disk or similar
813    fn flush(&mut self);
814}
815
816/// Stores nothing.
817#[derive(Clone, Default)]
818pub(crate) struct DummyStorage {}
819
820impl Storage for DummyStorage {
821    fn get_string(&self, _key: &str) -> Option<String> {
822        None
823    }
824
825    fn set_string(&mut self, _key: &str, _value: String) {}
826
827    fn flush(&mut self) {}
828}
829
830/// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key.
831#[cfg(feature = "ron")]
832pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &str) -> Option<T> {
833    profiling::function_scope!(key);
834    storage
835        .get_string(key)
836        .and_then(|value| match ron::from_str(&value) {
837            Ok(value) => Some(value),
838            Err(err) => {
839                // This happens on when we break the format, e.g. when updating egui.
840                log::debug!("Failed to decode RON: {err}");
841                None
842            }
843        })
844}
845
846/// Serialize the given value as [RON](https://github.com/ron-rs/ron) and store with the given key.
847#[cfg(feature = "ron")]
848pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) {
849    profiling::function_scope!(key);
850    match ron::ser::to_string(value) {
851        Ok(string) => storage.set_string(key, string),
852        Err(err) => log::error!("eframe failed to encode data using ron: {}", err),
853    }
854}
855
856/// [`Storage`] key used for app
857pub const APP_KEY: &str = "app";