Allow users to define a display refresh callback closure
This commit is contained in:
parent
bc7c06cc8c
commit
085495ebc4
4 changed files with 129 additions and 111 deletions
|
@ -1,4 +1,5 @@
|
||||||
use cstr_core::CString;
|
use cstr_core::CString;
|
||||||
|
use embedded_graphics::drawable;
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
use embedded_graphics_simulator::{
|
use embedded_graphics_simulator::{
|
||||||
|
@ -33,11 +34,44 @@ fn main() -> Result<(), LvError> {
|
||||||
// display. The buffer size can be set freely but 1/10 screen size is a good starting point.
|
// display. The buffer size can be set freely but 1/10 screen size is a good starting point.
|
||||||
const REFRESH_BUFFER_SIZE: usize = lvgl::DISP_HOR_RES * lvgl::DISP_VER_RES / 10;
|
const REFRESH_BUFFER_SIZE: usize = lvgl::DISP_HOR_RES * lvgl::DISP_VER_RES / 10;
|
||||||
static DRAW_BUFFER: DrawBuffer<REFRESH_BUFFER_SIZE> = DrawBuffer::new();
|
static DRAW_BUFFER: DrawBuffer<REFRESH_BUFFER_SIZE> = DrawBuffer::new();
|
||||||
|
//
|
||||||
|
// const NUMBER_OF_DISPLAYS: usize = 1;
|
||||||
|
// static DISPLAY_REGISTRY: DisplayRegistry<NUMBER_OF_DISPLAYS> = DisplayRegistry::empty();
|
||||||
|
// // static DISPLAY_REGISTRY: SingleDisplayRegistry = DisplayRegistry::empty();
|
||||||
|
// let display = DISPLAY_REGISTRY.register_shared(&DRAW_BUFFER, shared_native_display.clone())?;
|
||||||
|
|
||||||
// Register your native display with LVGL. We use the `Display::register_shared()` method here,
|
// Register your native display with LVGL. We use the `Display::register_shared()` method here,
|
||||||
// but that's because the Simulator needs a mutable reference to the display so it can draw
|
// but that's because the Simulator needs a mutable reference to the display so it can draw
|
||||||
// updates. On your embedded device code, you will use `Display::register()`.
|
// updates. On your embedded device code, you will use `Display::register()`.
|
||||||
let display = Display::register_shared(&DRAW_BUFFER, &shared_native_display)?;
|
let shared_disp_inner = shared_native_display.clone();
|
||||||
|
let display = Display::register_shared(&DRAW_BUFFER, move |update| {
|
||||||
|
// make this a `.into_pixels()` method in DisplayRefresh or `From<DisplayRefresh> for T where T: IntoIterator<Item = drawable::Pixel<C>>`
|
||||||
|
let area = &update.area;
|
||||||
|
let x1 = area.x1;
|
||||||
|
let x2 = area.x2;
|
||||||
|
let y1 = area.y1;
|
||||||
|
let y2 = area.y2;
|
||||||
|
|
||||||
|
let ys = y1..=y2;
|
||||||
|
let xs = (x1..=x2).enumerate();
|
||||||
|
let x_len = (x2 - x1 + 1) as usize;
|
||||||
|
|
||||||
|
// We use iterators here to ensure that the Rust compiler can apply all possible
|
||||||
|
// optimizations at compile time.
|
||||||
|
let pixels = ys
|
||||||
|
.enumerate()
|
||||||
|
.map(|(iy, y)| {
|
||||||
|
xs.clone().map(move |(ix, x)| {
|
||||||
|
let color_len = x_len * iy + ix;
|
||||||
|
let raw_color = update.colors[color_len];
|
||||||
|
drawable::Pixel(Point::new(x as i32, y as i32), raw_color.into())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let mut em_disp = shared_disp_inner.lock();
|
||||||
|
em_disp.draw_iter(pixels);
|
||||||
|
})?;
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = display.get_scr_act()?;
|
let mut screen = display.get_scr_act()?;
|
||||||
|
|
|
@ -39,29 +39,27 @@ impl Display {
|
||||||
Self { disp }
|
Self { disp }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register<T, C, const N: usize>(
|
// pub fn register<T, C, const N: usize>(
|
||||||
draw_buffer: &'static DrawBuffer<N>,
|
// draw_buffer: &'static DrawBuffer<N>,
|
||||||
native_display: T,
|
// native_display: T,
|
||||||
) -> Result<Self>
|
// ) -> Result<Self>
|
||||||
where
|
// where
|
||||||
T: DrawTarget<C>,
|
// T: DrawTarget<C>,
|
||||||
C: PixelColor + From<Color>,
|
// C: PixelColor + From<Color>,
|
||||||
{
|
// {
|
||||||
let mut display_diver = DisplayDriver::new(draw_buffer, native_display)?;
|
// let mut display_diver = DisplayDriver::new(draw_buffer, native_display)?;
|
||||||
Ok(disp_drv_register(&mut display_diver)?)
|
// Ok(disp_drv_register(&mut display_diver)?)
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn register_shared<T, C, const N: usize>(
|
pub fn register_shared<F, const N: usize>(
|
||||||
draw_buffer: &'static DrawBuffer<N>,
|
draw_buffer: &'static DrawBuffer<N>,
|
||||||
shared_native_display: &SharedNativeDisplay<T>,
|
display_update: F,
|
||||||
) -> Result<Self>
|
) -> Result<Self>
|
||||||
where
|
where
|
||||||
T: DrawTarget<C>,
|
F: FnMut(&DisplayRefresh<N>) + 'static,
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
{
|
||||||
let mut display_diver =
|
let mut display_diver = DisplayDriver::new_shared(draw_buffer, display_update)?;
|
||||||
DisplayDriver::new_shared(draw_buffer, Arc::clone(shared_native_display))?;
|
|
||||||
Ok(disp_drv_register(&mut display_diver)?)
|
Ok(disp_drv_register(&mut display_diver)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,61 +120,54 @@ impl<const N: usize> DrawBuffer<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DisplayDriver<T, C>
|
pub struct DisplayDriver {
|
||||||
where
|
|
||||||
T: DrawTarget<C>,
|
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
|
||||||
pub(crate) disp_drv: lvgl_sys::lv_disp_drv_t,
|
pub(crate) disp_drv: lvgl_sys::lv_disp_drv_t,
|
||||||
phantom_display: PhantomData<T>,
|
|
||||||
phantom_color: PhantomData<C>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, C> DisplayDriver<T, C>
|
impl DisplayDriver {
|
||||||
where
|
// pub fn new<const N: usize>(
|
||||||
T: DrawTarget<C>,
|
// draw_buffer: &'static DrawBuffer<N>,
|
||||||
C: PixelColor + From<Color>,
|
// native_display: T,
|
||||||
{
|
// ) -> Result<Self> {
|
||||||
pub fn new<const N: usize>(
|
// let mut disp_drv = unsafe {
|
||||||
draw_buffer: &'static DrawBuffer<N>,
|
// let mut inner = MaybeUninit::uninit();
|
||||||
native_display: T,
|
// lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
||||||
) -> Result<Self> {
|
// inner.assume_init()
|
||||||
let mut disp_drv = unsafe {
|
// };
|
||||||
let mut inner = MaybeUninit::uninit();
|
//
|
||||||
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
// // Safety: The variable `draw_buffer` is statically allocated, no need to worry about this being dropped.
|
||||||
inner.assume_init()
|
// disp_drv.buffer = draw_buffer
|
||||||
};
|
// .get_ptr()
|
||||||
|
// .map(|ptr| Box::into_raw(ptr) as *mut _)
|
||||||
// Safety: The variable `draw_buffer` is statically allocated, no need to worry about this being dropped.
|
// .ok_or(DisplayError::FailedToRegister)?;
|
||||||
disp_drv.buffer = draw_buffer
|
//
|
||||||
.get_ptr()
|
// let native_display = DisplayUserData {
|
||||||
.map(|ptr| Box::into_raw(ptr) as *mut _)
|
// display: native_display,
|
||||||
.ok_or(DisplayError::FailedToRegister)?;
|
// phantom: PhantomData,
|
||||||
|
// };
|
||||||
let native_display = DisplayUserData {
|
// disp_drv.user_data =
|
||||||
display: native_display,
|
// Box::into_raw(Box::new(native_display)) as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
||||||
phantom: PhantomData,
|
//
|
||||||
};
|
// // Sets trampoline pointer to the function implementation using the types (T, C) that
|
||||||
disp_drv.user_data =
|
// // are used in this instance of `DisplayDriver`.
|
||||||
Box::into_raw(Box::new(native_display)) as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
// disp_drv.flush_cb = Some(disp_flush_trampoline::<T, C>);
|
||||||
|
//
|
||||||
// Sets trampoline pointer to the function implementation using the types (T, C) that
|
// // We do not store any memory that can be accidentally deallocated by on the Rust side.
|
||||||
// are used in this instance of `DisplayDriver`.
|
// Ok(Self {
|
||||||
disp_drv.flush_cb = Some(disp_flush_trampoline::<T, C>);
|
// disp_drv,
|
||||||
|
// phantom_color: PhantomData,
|
||||||
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
// phantom_display: PhantomData,
|
||||||
Ok(Self {
|
// })
|
||||||
disp_drv,
|
// }
|
||||||
phantom_color: PhantomData,
|
|
||||||
phantom_display: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn new_shared<const N: usize>(
|
pub fn new_shared<F, const N: usize>(
|
||||||
draw_buffer: &'static DrawBuffer<N>,
|
draw_buffer: &'static DrawBuffer<N>,
|
||||||
shared_native_display: SharedNativeDisplay<T>,
|
display_update: F,
|
||||||
) -> Result<Self> {
|
) -> Result<Self>
|
||||||
|
where
|
||||||
|
F: FnMut(&DisplayRefresh<N>) + 'static,
|
||||||
|
{
|
||||||
let mut disp_drv = unsafe {
|
let mut disp_drv = unsafe {
|
||||||
let mut inner = MaybeUninit::uninit();
|
let mut inner = MaybeUninit::uninit();
|
||||||
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
||||||
|
@ -189,23 +180,15 @@ where
|
||||||
.map(|ptr| Box::into_raw(ptr) as *mut _)
|
.map(|ptr| Box::into_raw(ptr) as *mut _)
|
||||||
.ok_or(DisplayError::FailedToRegister)?;
|
.ok_or(DisplayError::FailedToRegister)?;
|
||||||
|
|
||||||
let native_display = SharedDisplayUserData {
|
|
||||||
display: shared_native_display,
|
|
||||||
phantom: PhantomData,
|
|
||||||
};
|
|
||||||
disp_drv.user_data =
|
disp_drv.user_data =
|
||||||
Box::into_raw(Box::new(native_display)) as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
Box::into_raw(Box::new(display_update)) as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
||||||
|
|
||||||
// Sets trampoline pointer to the function implementation using the types (T, C) that
|
// Sets trampoline pointer to the function implementation using the types (T, C) that
|
||||||
// are used in this instance of `DisplayDriver`.
|
// are used in this instance of `DisplayDriver`.
|
||||||
disp_drv.flush_cb = Some(shared_disp_flush_trampoline::<T, C>);
|
disp_drv.flush_cb = Some(shared_disp_flush_trampoline::<F, N>);
|
||||||
|
|
||||||
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
||||||
Ok(Self {
|
Ok(Self { disp_drv })
|
||||||
disp_drv,
|
|
||||||
phantom_color: PhantomData,
|
|
||||||
phantom_display: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,45 +252,48 @@ where
|
||||||
phantom: PhantomData<C>,
|
phantom: PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Area {
|
||||||
|
pub x1: i16,
|
||||||
|
pub x2: i16,
|
||||||
|
pub y1: i16,
|
||||||
|
pub y2: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DisplayRefresh<const N: usize> {
|
||||||
|
pub area: Area,
|
||||||
|
pub colors: [Color; N],
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
unsafe extern "C" fn shared_disp_flush_trampoline<T, C>(
|
unsafe extern "C" fn shared_disp_flush_trampoline<F, const N: usize>(
|
||||||
disp_drv: *mut lvgl_sys::lv_disp_drv_t,
|
disp_drv: *mut lvgl_sys::lv_disp_drv_t,
|
||||||
area: *const lvgl_sys::lv_area_t,
|
area: *const lvgl_sys::lv_area_t,
|
||||||
color_p: *mut lvgl_sys::lv_color_t,
|
color_p: *mut lvgl_sys::lv_color_t,
|
||||||
) where
|
) where
|
||||||
T: DrawTarget<C>,
|
F: FnMut(&DisplayRefresh<N>) + 'static,
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
{
|
||||||
let display_driver = *disp_drv;
|
let display_driver = *disp_drv;
|
||||||
if !display_driver.user_data.is_null() {
|
if !display_driver.user_data.is_null() {
|
||||||
let user_data = &mut *(display_driver.user_data as *mut SharedDisplayUserData<T, C>);
|
let callback = &mut *(display_driver.user_data as *mut F);
|
||||||
let x1 = (*area).x1;
|
|
||||||
let x2 = (*area).x2;
|
|
||||||
let y1 = (*area).y1;
|
|
||||||
let y2 = (*area).y2;
|
|
||||||
|
|
||||||
let ys = y1..=y2;
|
let mut colors = [Color::default(); N];
|
||||||
let xs = (x1..=x2).enumerate();
|
let mut color_len = 0;
|
||||||
let x_len = (x2 - x1 + 1) as usize;
|
for color in &mut colors {
|
||||||
|
let lv_color = unsafe { *color_p.add(color_len) };
|
||||||
|
*color = Color::from_raw(lv_color);
|
||||||
|
color_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// We use iterators here to ensure that the Rust compiler can apply all possible
|
let update = DisplayRefresh {
|
||||||
// optimizations at compile time.
|
area: Area {
|
||||||
let pixels = ys
|
x1: (*area).x1,
|
||||||
.enumerate()
|
x2: (*area).x2,
|
||||||
.map(|(iy, y)| {
|
y1: (*area).y1,
|
||||||
xs.clone().map(move |(ix, x)| {
|
y2: (*area).y2,
|
||||||
let color_len = x_len * iy + ix;
|
},
|
||||||
let lv_color = unsafe { *color_p.add(color_len) };
|
colors,
|
||||||
let raw_color = Color::from_raw(lv_color);
|
};
|
||||||
drawable::Pixel::<C>(Point::new(x as i32, y as i32), raw_color.into())
|
callback(&update);
|
||||||
})
|
|
||||||
})
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
let _ = user_data
|
|
||||||
.display
|
|
||||||
.try_lock()
|
|
||||||
.map(move |mut display| display.draw_iter(pixels));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate to LVGL that we are ready with the flushing
|
// Indicate to LVGL that we are ready with the flushing
|
||||||
|
|
|
@ -14,9 +14,7 @@ pub enum CoreError {
|
||||||
type Result<T> = result::Result<T, CoreError>;
|
type Result<T> = result::Result<T, CoreError>;
|
||||||
|
|
||||||
/// Register own buffer
|
/// Register own buffer
|
||||||
pub(crate) fn disp_drv_register<C: PixelColor + From<Color>, T: DrawTarget<C>>(
|
pub(crate) fn disp_drv_register(disp_drv: &mut DisplayDriver) -> Result<Display> {
|
||||||
disp_drv: &mut DisplayDriver<T, C>,
|
|
||||||
) -> Result<Display> {
|
|
||||||
let disp_ptr = unsafe { lvgl_sys::lv_disp_drv_register(&mut disp_drv.disp_drv as *mut _) };
|
let disp_ptr = unsafe { lvgl_sys::lv_disp_drv_register(&mut disp_drv.disp_drv as *mut _) };
|
||||||
Ok(Display::from_raw(
|
Ok(Display::from_raw(
|
||||||
NonNull::new(disp_ptr).ok_or(CoreError::OperationFailed)?,
|
NonNull::new(disp_ptr).ok_or(CoreError::OperationFailed)?,
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl From<DisplayError> for LvError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
pub(crate) raw: lvgl_sys::lv_color_t,
|
pub(crate) raw: lvgl_sys::lv_color_t,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue