Revising the public API for better flexibility

This commit is contained in:
Rafael Caricio 2021-05-29 20:03:36 +02:00
parent 56cb470dc8
commit 95c7f00e20
Signed by: rafaelcaricio
GPG key ID: 3C86DBCE8E93C947
7 changed files with 209 additions and 5 deletions

26
examples/app.rs Normal file
View file

@ -0,0 +1,26 @@
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::*;
use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window};
use lvgl;
use lvgl::display::{Display, DisplayBuffer, DisplayDriver};
type ColorSpace = Rgb565;
fn main() {
let mut embedded_graphics_display: SimulatorDisplay<ColorSpace> = SimulatorDisplay::new(
Size::new(lvgl_sys::LV_HOR_RES_MAX, lvgl_sys::LV_VER_RES_MAX),
);
let output_settings = OutputSettingsBuilder::new().scale(2).build();
let mut window = Window::new("App Example", &output_settings);
// LVGL usage
lvgl::init();
let mut display_diver: DisplayDriver<ColorSpace> =
DisplayDriver::new(DisplayBuffer::new(), |pixels| {
// Here we draw to the external display
let _ = embedded_graphics_display.draw_iter(pixels);
});
let _display = lvgl::disp_drv_register(&mut display_diver).unwrap();
}

View file

@ -114,6 +114,7 @@ fn main() {
let bindings = bindgen::Builder::default()
.header(shims_dir.join("lvgl_sys.h").to_str().unwrap())
.generate_comments(false)
.derive_default(true)
.layout_tests(false)
.use_core()
.rustfmt_bindings(true)

View file

@ -32,6 +32,11 @@ lvgl-sys = { version = "0.5.2", path = "../lvgl-sys" }
embedded-graphics-simulator = "0.2.1"
heapless = "0.5.5"
[[example]]
name = "app"
path = "../examples/app.rs"
required-features = ["alloc"]
[[example]]
name = "demo"
path = "../examples/demo.rs"

150
lvgl/src/display.rs Normal file
View file

@ -0,0 +1,150 @@
use crate::{Color, LvError, LvResult, Obj};
use core::marker::PhantomData;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr;
use core::ptr::NonNull;
use embedded_graphics::drawable;
use embedded_graphics::prelude::*;
// TODO: Make this an external configuration
const REFRESH_BUFFER_LEN: usize = 2;
// Declare a buffer for the refresh rate
pub(crate) const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
pub enum DisplayError {
FailedToRegister,
NotRegistered,
}
#[derive(Copy, Clone)]
pub struct Display {
disp: NonNull<lvgl_sys::lv_disp_t>,
}
impl Display {
pub(crate) fn from_raw(disp: NonNull<lvgl_sys::lv_disp_t>) -> Self {
Self { disp }
}
}
#[derive(Copy, Clone)]
pub struct DefaultDisplay {}
impl DefaultDisplay {
pub fn get_screen_active() -> Result<Obj, DisplayError> {
Err(DisplayError::NotRegistered)
}
}
#[derive(Copy, Clone)]
pub struct DisplayBuffer {
disp_buf: lvgl_sys::lv_disp_buf_t,
}
impl DisplayBuffer {
pub fn new() -> Self {
let disp_buf = unsafe {
let mut disp_buf = MaybeUninit::uninit();
let mut refresh_buffer = ManuallyDrop::new([lvgl_sys::lv_color_t::default(); BUF_SIZE]);
lvgl_sys::lv_disp_buf_init(
disp_buf.as_mut_ptr(),
refresh_buffer.as_mut_ptr() as *mut cty::c_void,
ptr::null_mut(),
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
);
disp_buf.assume_init()
};
Self { disp_buf }
}
}
#[derive(Copy, Clone)]
pub struct DisplayDriver<C>
where
C: PixelColor + From<Color>,
{
pub(crate) disp_drv: lvgl_sys::lv_disp_drv_t,
phantom: PhantomData<C>,
}
impl<C> DisplayDriver<C>
where
C: PixelColor + From<Color>,
{
pub fn new<I, F>(display_buffer: DisplayBuffer, callback: F) -> Self
where
I: IntoIterator<Item = drawable::Pixel<C>>,
F: FnMut(I) + 'static,
{
let mut disp_buf = ManuallyDrop::new(display_buffer.disp_buf);
let mut callback = ManuallyDrop::new(callback);
let mut disp_drv = unsafe {
let mut disp_drv = MaybeUninit::uninit();
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
disp_drv.assume_init()
};
disp_drv.buffer = &mut *disp_buf as *mut lvgl_sys::lv_disp_buf_t;
disp_drv.user_data = &mut callback as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
disp_drv.flush_cb = Some(disp_flush_trampoline::<C, I, F>);
Self {
disp_drv,
phantom: PhantomData,
}
}
}
// impl Default for DisplayDriver {
// fn default() -> Self {
// Self::new(DisplayBuffer::new())
// }
// }
pub struct DisplayDriverBuilder {
disp_buf: Option<DisplayBuffer>,
}
impl DisplayDriverBuilder {
pub fn with_callback() {}
}
unsafe extern "C" fn disp_flush_trampoline<C, I, F>(
disp_drv: *mut lvgl_sys::lv_disp_drv_t,
area: *const lvgl_sys::lv_area_t,
color_p: *mut lvgl_sys::lv_color_t,
) where
C: PixelColor + From<Color>,
I: IntoIterator<Item = drawable::Pixel<C>>,
F: FnMut(I) + 'static,
{
let display_driver = *disp_drv;
if !display_driver.user_data.is_null() {
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 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 lv_color = unsafe { *color_p.add(color_len) };
let raw_color = Color::from_raw(lv_color);
drawable::Pixel(Point::new(x as i32, y as i32), raw_color.into())
})
})
.flatten();
callback(pixels);
}
}

16
lvgl/src/functions.rs Normal file
View file

@ -0,0 +1,16 @@
use crate::display::{Display, DisplayDriver, DisplayError};
use crate::Color;
use core::ptr::NonNull;
use embedded_graphics::drawable;
use embedded_graphics::prelude::*;
pub fn disp_drv_register<C: PixelColor + From<Color>>(
disp_drv: &mut DisplayDriver<C>,
) -> Result<Display, DisplayError> {
let disp_ptr = unsafe {
lvgl_sys::lv_disp_drv_register(&mut disp_drv.disp_drv as *mut lvgl_sys::lv_disp_drv_t)
};
Ok(Display::from_raw(
NonNull::new(disp_ptr).ok_or(DisplayError::FailedToRegister)?,
))
}

View file

@ -31,6 +31,8 @@ use ::alloc::boxed::Box;
#[cfg(feature = "lvgl_alloc")]
mod allocator;
pub mod display;
mod functions;
mod support;
mod ui;
#[macro_use]
@ -47,6 +49,7 @@ pub(crate) mod mem;
#[cfg(not(feature = "lvgl_alloc"))]
use crate::mem::Box;
pub use functions::*;
pub use lv_core::*;
pub use support::*;
pub use ui::*;
@ -56,7 +59,7 @@ use core::sync::atomic::{AtomicBool, Ordering};
// Initialize LVGL only once.
static LVGL_INITIALIZED: AtomicBool = AtomicBool::new(false);
pub(crate) fn lvgl_init() {
pub fn init() {
if LVGL_INITIALIZED
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()

View file

@ -1,7 +1,7 @@
use crate::Box;
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicBool, Ordering};
@ -47,7 +47,7 @@ where
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
crate::lvgl_init();
crate::init();
Ok(Self {
_not_sync: PhantomData,
display_data: None,
@ -80,10 +80,13 @@ where
// Basic initialization of the display driver
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
let mut disp_drv = Box::new(disp_drv.assume_init());
// Assign the buffer to the display
// Assign the buffer to the display, the memory "leaks" here since
// the `disp_drv` is dropped in the end of this method. This is not a problem
// since this should live for the whole lifetime of the program anyways.
disp_drv.buffer = Box::into_raw(Box::new(disp_buf.assume_init()));
// Set your driver function
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>);
// The memory of `display_data` is kept because of the reference in `self`
disp_drv.user_data = &mut self.display_data as *mut _ as *mut cty::c_void;
// We need to remember to deallocate the `disp_drv` memory when dropping UI
lvgl_sys::lv_disp_drv_register(Box::into_raw(disp_drv));
@ -110,7 +113,7 @@ where
}
}
pub fn event_send<W>(&mut self, obj: &mut W, event: Event<W::SpecialEvent>) -> LvResult<()>
pub fn event_send<W>(&self, obj: &mut W, event: Event<W::SpecialEvent>) -> LvResult<()>
where
W: Widget,
{