1use embedded_graphics_core::pixelcolor::Rgb565;
19use embedded_graphics_framebuf::{FrameBuf, backends::DMACapableFrameBufferBackend};
20
21pub use crate::present::PresentRegion as DisplayRegion;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum DisplayError {
30 Busy,
32 HardwareError,
34 InvalidBuffer,
36}
37
38pub struct TransferError<FB>
43where
44 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
45{
46 pub framebuffer: FrameBuf<Rgb565, FB>,
48 pub error: DisplayError,
50}
51
52impl<FB> core::fmt::Debug for TransferError<FB>
53where
54 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
55{
56 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57 f.debug_struct("TransferError")
58 .field("error", &self.error)
59 .finish_non_exhaustive()
60 }
61}
62
63pub trait DmaTransfer {
72 type Buffer;
74
75 fn is_done(&self) -> bool;
77
78 fn wait(self) -> Self::Buffer;
83}
84
85pub trait AsyncDmaTransfer: DmaTransfer {
90 type WaitFuture: core::future::Future<Output = Self::Buffer>;
92
93 fn wait_async(self) -> Self::WaitFuture;
95}
96
97pub trait DisplayBackend<const W: usize, const H: usize, FB>
106where
107 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
108{
109 type Transfer: DmaTransfer<Buffer = FrameBuf<Rgb565, FB>>;
111
112 fn start_dma_transfer(
114 &mut self,
115 framebuffer: FrameBuf<Rgb565, FB>,
116 ) -> Result<Self::Transfer, TransferError<FB>>;
117
118 fn start_dma_transfer_region(
123 &mut self,
124 framebuffer: FrameBuf<Rgb565, FB>,
125 _region: DisplayRegion,
126 ) -> Result<Self::Transfer, TransferError<FB>> {
127 self.start_dma_transfer(framebuffer)
128 }
129}
130
131pub struct CompletedTransfer<FB>
135where
136 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
137{
138 framebuffer: Option<FrameBuf<Rgb565, FB>>,
139}
140
141impl<FB> DmaTransfer for CompletedTransfer<FB>
142where
143 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
144{
145 type Buffer = FrameBuf<Rgb565, FB>;
146
147 fn is_done(&self) -> bool {
148 true
149 }
150
151 fn wait(mut self) -> FrameBuf<Rgb565, FB> {
152 self.framebuffer
153 .take()
154 .expect("CompletedTransfer polled after completion")
155 }
156}
157
158impl<FB> core::future::Future for CompletedTransfer<FB>
159where
160 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
161{
162 type Output = FrameBuf<Rgb565, FB>;
163
164 fn poll(
165 self: core::pin::Pin<&mut Self>,
166 _cx: &mut core::task::Context<'_>,
167 ) -> core::task::Poll<Self::Output> {
168 let buf = unsafe { self.get_unchecked_mut() }
169 .framebuffer
170 .take()
171 .expect("CompletedTransfer polled after completion");
172 core::task::Poll::Ready(buf)
173 }
174}
175
176impl<FB> AsyncDmaTransfer for CompletedTransfer<FB>
177where
178 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
179{
180 type WaitFuture = Self;
181
182 fn wait_async(self) -> Self::WaitFuture {
183 self
184 }
185}
186
187pub struct SimulatorBackend;
189
190impl SimulatorBackend {
191 pub fn new() -> Self {
192 Self
193 }
194}
195
196impl Default for SimulatorBackend {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201
202impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for SimulatorBackend
203where
204 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
205{
206 type Transfer = CompletedTransfer<FB>;
207
208 fn start_dma_transfer(
209 &mut self,
210 framebuffer: FrameBuf<Rgb565, FB>,
211 ) -> Result<CompletedTransfer<FB>, TransferError<FB>> {
212 Ok(CompletedTransfer {
213 framebuffer: Some(framebuffer),
214 })
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 extern crate std;
221 use super::*;
222 use core::cell::Cell;
223 use embedded_graphics_core::pixelcolor::RgbColor;
224 use embedded_graphics_framebuf::backends::EndianCorrectedBuffer;
225 use std::vec;
226
227 type TestBackend = EndianCorrectedBuffer<'static, Rgb565>;
228
229 fn make_fb<const W: usize, const H: usize>() -> FrameBuf<Rgb565, TestBackend> {
230 use embedded_graphics_framebuf::backends::EndianCorrection;
231 let data: &'static mut [Rgb565] = vec![Rgb565::BLACK; W * H].leak();
232 FrameBuf::new(
233 EndianCorrectedBuffer::new(data, EndianCorrection::ToLittleEndian),
234 W,
235 H,
236 )
237 }
238
239 struct RegionTransfer<FB: DMACapableFrameBufferBackend<Color = Rgb565>> {
240 framebuffer: Option<FrameBuf<Rgb565, FB>>,
241 }
242
243 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> DmaTransfer for RegionTransfer<FB> {
244 type Buffer = FrameBuf<Rgb565, FB>;
245 fn is_done(&self) -> bool {
246 true
247 }
248 fn wait(mut self) -> FrameBuf<Rgb565, FB> {
249 self.framebuffer.take().unwrap()
250 }
251 }
252
253 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> core::future::Future for RegionTransfer<FB> {
254 type Output = FrameBuf<Rgb565, FB>;
255 fn poll(
256 self: core::pin::Pin<&mut Self>,
257 _cx: &mut core::task::Context<'_>,
258 ) -> core::task::Poll<Self::Output> {
259 core::task::Poll::Ready(
260 unsafe { self.get_unchecked_mut() }
261 .framebuffer
262 .take()
263 .unwrap(),
264 )
265 }
266 }
267
268 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> AsyncDmaTransfer for RegionTransfer<FB> {
269 type WaitFuture = Self;
270 fn wait_async(self) -> Self::WaitFuture {
271 self
272 }
273 }
274
275 struct RegionTrackingBackend {
276 region_calls: Cell<usize>,
277 }
278
279 impl RegionTrackingBackend {
280 fn new() -> Self {
281 Self {
282 region_calls: Cell::new(0),
283 }
284 }
285 }
286
287 impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for RegionTrackingBackend
288 where
289 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
290 {
291 type Transfer = RegionTransfer<FB>;
292
293 fn start_dma_transfer(
294 &mut self,
295 framebuffer: FrameBuf<Rgb565, FB>,
296 ) -> Result<RegionTransfer<FB>, TransferError<FB>> {
297 Ok(RegionTransfer {
298 framebuffer: Some(framebuffer),
299 })
300 }
301
302 fn start_dma_transfer_region(
303 &mut self,
304 framebuffer: FrameBuf<Rgb565, FB>,
305 _region: DisplayRegion,
306 ) -> Result<RegionTransfer<FB>, TransferError<FB>> {
307 self.region_calls.set(self.region_calls.get() + 1);
308 Ok(RegionTransfer {
309 framebuffer: Some(framebuffer),
310 })
311 }
312 }
313
314 #[test]
315 fn test_simulator_backend_transfer_completes_immediately() {
316 let mut backend = SimulatorBackend::new();
317 let fb = make_fb::<2, 2>();
318 let xfer = <SimulatorBackend as DisplayBackend<2, 2, TestBackend>>::start_dma_transfer(
319 &mut backend,
320 fb,
321 )
322 .unwrap();
323 assert!(xfer.is_done());
324 let _fb = xfer.wait();
325 }
326
327 #[test]
328 fn test_region_tracking_backend_counts_region_transfers() {
329 let mut backend = RegionTrackingBackend::new();
330 let fb = make_fb::<2, 2>();
331 let region = DisplayRegion::new(0, 0, 1, 1);
332 let xfer = <RegionTrackingBackend as DisplayBackend<2, 2, TestBackend>>::start_dma_transfer_region(
333 &mut backend,
334 fb,
335 region,
336 )
337 .unwrap();
338 assert_eq!(backend.region_calls.get(), 1);
339 let _ = xfer.wait();
340 }
341}