Skip to main content

egui_sdl3/
lib.rs

1//! # egui-sdl3
2//!
3//! Integration between [`egui`](https://github.com/emilk/egui) and
4//! [`sdl3`](https://github.com/vhspace/sdl3-rs).
5//!
6//! ## Features
7//! - Translate SDL3 events into [`egui`] events.
8//! - Handle [`egui::PlatformOutput`] (clipboard, cursor updates, links).
9//! - Render with OpenGL via [`glow`] (`glow-backend` feature).
10//! - Render with the SDL3 software renderer via [`sdl3::render::Canvas`] (`canvas-backend` feature).
11//!
12//! ## Usage
13//! ```no_run
14//! // Create SDL3 window:
15//! let sdl = sdl3::init().unwrap();
16//! let video = sdl.video().unwrap();
17//! let window = video.window("Egui SDL3 Canvas", 800, 600).build().unwrap();
18//! // Create egui renderer:
19//! let mut egui = egui_sdl3::EguiCanvas::new(window);
20//! let mut event_pump = sdl.event_pump().unwrap();
21//! loop {
22//!    // Feed SDL3 events into egui:
23//!    for event in event_pump.poll_iter() {
24//!        egui.on_event(&event);
25//!    }
26//!    // Call `run` + `paint` each frame:
27//!    egui.run(|ctx: &egui::Context| {});
28//!    egui.paint();
29//!    egui.present();
30//!    std::thread::sleep(std::time::Duration::from_secs_f64(1.0 / 60.0));
31//!}
32//! ```
33
34pub use egui;
35#[cfg(feature = "glow-backend")]
36pub use egui_glow;
37pub use sdl3;
38
39#[cfg(feature = "canvas-backend")]
40pub mod canvas;
41#[cfg(feature = "glow-backend")]
42pub mod glow;
43pub mod state;
44#[cfg(feature = "wgpu-backend")]
45pub mod wgpu;
46
47#[cfg(feature = "canvas-backend")]
48pub use canvas::EguiCanvas;
49#[cfg(feature = "glow-backend")]
50pub use glow::*;
51pub use state::*;
52#[cfg(feature = "wgpu-backend")]
53pub use wgpu::EguiWgpu;
54
55/// The results of running one frame of `egui`.
56///
57/// `EguiRunOutput` collects the renderable shapes, texture updates, and scale
58/// factor from a single `egui` run. It also provides convenience methods for
59/// updating its contents from an `egui::Context` and for draining the data
60/// when it is time to render.
61///
62/// This is typically created once per backend instance and reused across frames.
63pub struct EguiRunOutput {
64    /// The clipped shapes that should be rendered for the current frame.
65    ///
66    /// This is produced by egui’s tessellation step and represents what should
67    /// be drawn to the screen.
68    pub shapes: Vec<egui::epaint::ClippedShape>,
69
70    /// The logical-to-physical pixel scaling factor used by egui in this frame.
71    ///
72    /// Backends should respect this when converting coordinates to pixels.
73    pub pixels_per_point: f32,
74
75    /// The delta of texture updates required for this frame.
76    ///
77    /// Includes new textures to upload and old textures to free.
78    pub textures_delta: egui::TexturesDelta,
79
80    /// How long until egui wants the ROOT viewport repainted, as reported by
81    /// the last [`Self::update`] (egui's `ViewportOutput::repaint_delay`).
82    ///
83    /// `Duration::ZERO` means egui needs another frame *immediately* — e.g. the
84    /// frame just run was a sizing pass for a freshly shown [`egui::Area`], whose
85    /// real (positioned, visible) frame must follow. `Duration::MAX` means egui
86    /// is idle and the backend may block on input. Event-driven backends should
87    /// fold this into their idle wait so animations and first-frame layout show
88    /// without an extra input event.
89    pub repaint_delay: std::time::Duration,
90}
91
92impl Default for EguiRunOutput {
93    /// Creates an empty `EguiRunOutput` with no shapes, no texture updates,
94    /// and a scale factor of `1.0`.
95    fn default() -> Self {
96        Self {
97            shapes: Default::default(),
98            pixels_per_point: 1.0,
99            textures_delta: Default::default(),
100            repaint_delay: std::time::Duration::MAX,
101        }
102    }
103}
104
105impl EguiRunOutput {
106    /// Run `egui` for one frame and update this output with the results.
107    ///
108    /// # Parameters
109    /// - `ctx`: The [`egui::Context`] used to run the UI.
110    /// - `state`: A backend state that provides input for egui and
111    ///   handles platform output (clipboard, cursor, etc.).
112    /// - `run_ui`: A closure that builds the UI using the given `egui::Context`.
113    ///
114    /// # Behavior
115    /// - Takes input events from `state`.
116    /// - Runs egui with the provided `run_ui` closure.
117    /// - Handles platform output via `state`.
118    /// - Stores the frame’s shapes, texture updates, and scale factor
119    ///   in this `EguiRunOutput`.
120    #[inline]
121    pub fn update(
122        &mut self,
123        ctx: &egui::Context,
124        state: &mut State,
125        mut run_ui: impl FnMut(&egui::Context),
126    ) {
127        let raw_input = state.take_egui_input();
128        // Our public API hands the caller an `&egui::Context`, so we drive egui through
129        // `run_ui` (which internally handles multi-pass layout) and bridge the root `Ui`
130        // back to its context. This keeps the `FnMut(&egui::Context)` closure contract
131        // while avoiding the deprecated `Context::run`.
132        let egui::FullOutput {
133            platform_output,
134            viewport_output,
135            textures_delta,
136            shapes,
137            pixels_per_point,
138        } = ctx.run_ui(raw_input, |ui| run_ui(ui.ctx()));
139        state.handle_platform_output(platform_output);
140
141        self.shapes = shapes;
142        self.textures_delta.append(textures_delta);
143        self.pixels_per_point = pixels_per_point;
144        // Surface egui's own repaint timing for the ROOT viewport so event-driven
145        // backends don't drop it: a sizing pass for a freshly shown anchored Area
146        // reports `ZERO` here, asking for the follow-up frame that actually paints
147        // it. `MAX` if egui didn't report (idle — wait on input).
148        self.repaint_delay = viewport_output
149            .get(&egui::ViewportId::ROOT)
150            .map_or(std::time::Duration::MAX, |v| v.repaint_delay);
151    }
152
153    /// Take ownership of the texture updates and shapes for the current frame.
154    ///
155    /// This clears both fields in the struct, leaving them empty for the next frame.
156    ///
157    /// # Returns
158    /// - `(textures_delta, shapes)` where:
159    ///   - `textures_delta`: The [`egui::TexturesDelta`] with texture uploads/free requests.
160    ///   - `shapes`: The tessellated shapes that should be rendered.
161    #[inline]
162    pub fn take(&mut self) -> (egui::TexturesDelta, Vec<egui::epaint::ClippedShape>) {
163        let textures_delta = std::mem::take(&mut self.textures_delta);
164        let shapes = std::mem::take(&mut self.shapes);
165
166        (textures_delta, shapes)
167    }
168}