1use embedded_graphics_core::pixelcolor::Rgb565;
9use embedded_graphics_framebuf::{FrameBuf, backends::DMACapableFrameBufferBackend};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct DisplayRegion {
14 pub x: usize,
15 pub y: usize,
16 pub width: usize,
17 pub height: usize,
18}
19
20impl DisplayRegion {
21 pub const fn new(x: usize, y: usize, width: usize, height: usize) -> Self {
22 Self {
23 x,
24 y,
25 width,
26 height,
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum DisplayError {
34 Busy,
36 HardwareError,
38 InvalidBuffer,
40}
41
42pub trait DisplayBackend<const W: usize, const H: usize, FB>
47where
48 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
49{
50 fn start_dma_transfer(
62 &mut self,
63 framebuffer: &FrameBuf<Rgb565, FB>,
64 ) -> Result<(), DisplayError>;
65
66 fn start_dma_transfer_region(
70 &mut self,
71 framebuffer: &FrameBuf<Rgb565, FB>,
72 _region: DisplayRegion,
73 ) -> Result<(), DisplayError> {
74 self.start_dma_transfer(framebuffer)
75 }
76
77 fn wait_for_dma(&mut self);
81
82 fn is_dma_ready(&self) -> bool;
87
88 fn present(&mut self, framebuffer: &FrameBuf<Rgb565, FB>) -> Result<(), DisplayError> {
98 self.wait_for_dma();
99 self.start_dma_transfer(framebuffer)
100 }
101
102 fn present_region(
106 &mut self,
107 framebuffer: &FrameBuf<Rgb565, FB>,
108 region: DisplayRegion,
109 ) -> Result<(), DisplayError> {
110 self.wait_for_dma();
111 self.start_dma_transfer_region(framebuffer, region)
112 }
113}
114
115pub struct SimulatorBackend {
123 }
125
126impl SimulatorBackend {
127 pub fn new() -> Self {
129 Self {}
130 }
131}
132
133impl Default for SimulatorBackend {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for SimulatorBackend
140where
141 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
142{
143 fn start_dma_transfer(
144 &mut self,
145 _framebuffer: &FrameBuf<Rgb565, FB>,
146 ) -> Result<(), DisplayError> {
147 Ok(())
149 }
150
151 fn wait_for_dma(&mut self) {
152 }
154
155 fn is_dma_ready(&self) -> bool {
156 true
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 extern crate std;
164 use super::*;
165 use core::cell::Cell;
166 use embedded_graphics_core::pixelcolor::RgbColor;
167 use embedded_graphics_framebuf::backends::EndianCorrectedBuffer;
168 use std::vec;
169
170 type TestBackend = EndianCorrectedBuffer<'static, Rgb565>;
172
173 struct RegionTrackingBackend {
174 region_calls: Cell<usize>,
175 }
176
177 impl RegionTrackingBackend {
178 fn new() -> Self {
179 Self {
180 region_calls: Cell::new(0),
181 }
182 }
183 }
184
185 impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for RegionTrackingBackend
186 where
187 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
188 {
189 fn start_dma_transfer(
190 &mut self,
191 _framebuffer: &FrameBuf<Rgb565, FB>,
192 ) -> Result<(), DisplayError> {
193 Ok(())
194 }
195
196 fn start_dma_transfer_region(
197 &mut self,
198 _framebuffer: &FrameBuf<Rgb565, FB>,
199 _region: DisplayRegion,
200 ) -> Result<(), DisplayError> {
201 self.region_calls.set(self.region_calls.get() + 1);
202 Ok(())
203 }
204
205 fn wait_for_dma(&mut self) {}
206
207 fn is_dma_ready(&self) -> bool {
208 true
209 }
210 }
211
212 #[test]
213 fn test_simulator_backend_creation() {
214 let backend = SimulatorBackend::new();
215 assert!(<SimulatorBackend as DisplayBackend<
218 320,
219 240,
220 TestBackend,
221 >>::is_dma_ready(&backend));
222 }
223
224 #[test]
225 fn test_simulator_backend_always_ready() {
226 let mut backend = SimulatorBackend::new();
227
228 assert!(<SimulatorBackend as DisplayBackend<
230 320,
231 240,
232 TestBackend,
233 >>::is_dma_ready(&backend));
234
235 <SimulatorBackend as DisplayBackend<320, 240, TestBackend>>::wait_for_dma(&mut backend);
237 assert!(<SimulatorBackend as DisplayBackend<
238 320,
239 240,
240 TestBackend,
241 >>::is_dma_ready(&backend));
242 }
243
244 #[test]
245 fn test_present_region_calls_region_transfer() {
246 let mut backend = RegionTrackingBackend::new();
247 let data = vec![Rgb565::BLACK; 4].leak();
248 let fb = FrameBuf::new(
249 EndianCorrectedBuffer::new(
250 data,
251 embedded_graphics_framebuf::backends::EndianCorrection::ToLittleEndian,
252 ),
253 2,
254 2,
255 );
256 let region = DisplayRegion::new(0, 0, 1, 1);
257 <RegionTrackingBackend as DisplayBackend<2, 2, TestBackend>>::present_region(
258 &mut backend,
259 &fb,
260 region,
261 )
262 .unwrap();
263 assert_eq!(backend.region_calls.get(), 1);
264 }
265}