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";