Skip to main content

embedded_gui/context/
present.rs

1//! Present helpers for swap-chain integration.
2
3use embedded_graphics_framebuf::backends::DMACapableFrameBufferBackend;
4
5use crate::{
6    GuiContext,
7    display_backend::{AsyncDmaTransfer, DisplayBackend, DisplayError},
8    swapchain::SwapChain,
9};
10
11impl<'a, const NODES: usize, const EVENTS: usize, const DIRTY: usize>
12    GuiContext<'a, NODES, EVENTS, DIRTY>
13{
14    /// Non-blocking present of the dirty bounding box after rendering into the back buffer.
15    ///
16    /// Returns [`DisplayError::Busy`] if a DMA transfer is still in flight. For multiple
17    /// disjoint dirty rects, this issues one partial DMA covering the bounding box.
18    pub fn try_present_dirty<const W: usize, const H: usize, FB, B>(
19        &self,
20        swap: &mut SwapChain<W, H, FB, B>,
21    ) -> Result<(), DisplayError>
22    where
23        FB: DMACapableFrameBufferBackend<Color = embedded_graphics_core::pixelcolor::Rgb565>,
24        B: DisplayBackend<W, H, FB>,
25    {
26        let Some(region) = self.bounding_present_region() else {
27            return Ok(());
28        };
29        if region.is_empty() {
30            return Ok(());
31        }
32        swap.try_present_region(region)
33    }
34
35    /// Async present of the dirty bounding box after rendering into the back buffer.
36    pub async fn present_dirty_async<const W: usize, const H: usize, FB, B>(
37        &self,
38        swap: &mut SwapChain<W, H, FB, B>,
39    ) -> Result<(), DisplayError>
40    where
41        FB: DMACapableFrameBufferBackend<Color = embedded_graphics_core::pixelcolor::Rgb565>,
42        B: DisplayBackend<W, H, FB>,
43        B::Transfer: AsyncDmaTransfer<
44            Buffer = embedded_graphics_framebuf::FrameBuf<
45                embedded_graphics_core::pixelcolor::Rgb565,
46                FB,
47            >,
48        >,
49    {
50        let Some(region) = self.bounding_present_region() else {
51            return Ok(());
52        };
53        if region.is_empty() {
54            return Ok(());
55        }
56        swap.present_region_async(region).await
57    }
58
59    /// Full-frame non-blocking present (ignores dirty region geometry).
60    pub fn try_present_frame<const W: usize, const H: usize, FB, B>(
61        &self,
62        swap: &mut SwapChain<W, H, FB, B>,
63    ) -> Result<(), DisplayError>
64    where
65        FB: DMACapableFrameBufferBackend<Color = embedded_graphics_core::pixelcolor::Rgb565>,
66        B: DisplayBackend<W, H, FB>,
67    {
68        let _ = self;
69        swap.try_present()
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    extern crate std;
76    use super::*;
77    use crate::{
78        display_backend::SimulatorBackend, geometry::Rect, style::Style,
79        swapchain::StandardSwapChain,
80    };
81    use embedded_graphics_core::pixelcolor::{Rgb565, RgbColor};
82    use std::vec;
83
84    fn make_swap() -> StandardSwapChain<64, 64, SimulatorBackend> {
85        let fb0: &'static mut [Rgb565] = vec![Rgb565::BLACK; 64 * 64].leak();
86        let fb1: &'static mut [Rgb565] = vec![Rgb565::BLACK; 64 * 64].leak();
87        StandardSwapChain::from_static_slices(fb0, fb1, false, SimulatorBackend::new())
88    }
89
90    #[test]
91    fn try_present_dirty_uses_bounding_region() {
92        let mut gui = GuiContext::<4, 4, 4>::new(Rect::new(0, 0, 64, 64));
93        gui.add_label(Rect::new(4, 4, 20, 8), "hi", Style::label())
94            .unwrap();
95        let mut swap = make_swap();
96        assert!(gui.try_present_dirty(&mut swap).is_ok());
97    }
98}