Remove alloc dependency
This commit is contained in:
parent
657640d206
commit
8b2879f93b
13 changed files with 429 additions and 286 deletions
|
@ -1,18 +1,19 @@
|
||||||
|
use cstr_core::CString;
|
||||||
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::{
|
||||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||||
};
|
};
|
||||||
use lvgl::style::Style;
|
use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Bar, BarPart, Label, LabelAlign};
|
use lvgl::widgets::{Bar, Label, LabelAlign};
|
||||||
use lvgl::{self, Align, Animation, Color, DisplayDriver, Event, LvError, Part, State, Widget, UI};
|
use lvgl::{self, Align, Animation, Color, Event, LvError, Part, State, Widget, UI};
|
||||||
use lvgl_sys;
|
use lvgl_sys;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
lvgl_sys::LV_HOR_RES_MAX,
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
lvgl_sys::LV_VER_RES_MAX,
|
||||||
));
|
));
|
||||||
|
@ -23,8 +24,7 @@ fn main() -> Result<(), LvError> {
|
||||||
let mut ui = UI::init()?;
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
// Implement and register your display:
|
// Implement and register your display:
|
||||||
let display_driver = DisplayDriver::new(&mut display);
|
ui.disp_drv_register(display).unwrap();
|
||||||
ui.disp_drv_register(display_driver);
|
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = ui.scr_act()?;
|
let mut screen = ui.scr_act()?;
|
||||||
|
@ -39,6 +39,9 @@ fn main() -> Result<(), LvError> {
|
||||||
bar.set_size(175, 20)?;
|
bar.set_size(175, 20)?;
|
||||||
bar.set_align(&mut screen, Align::Center, 0, 10)?;
|
bar.set_align(&mut screen, Align::Center, 0, 10)?;
|
||||||
bar.set_range(0, 100)?;
|
bar.set_range(0, 100)?;
|
||||||
|
bar.on_event(|_b, _e| {
|
||||||
|
println!("received");
|
||||||
|
})?;
|
||||||
|
|
||||||
// // Set the indicator style for the bar object
|
// // Set the indicator style for the bar object
|
||||||
let mut ind_style = Style::default();
|
let mut ind_style = Style::default();
|
||||||
|
@ -46,7 +49,7 @@ fn main() -> Result<(), LvError> {
|
||||||
bar.add_style(Part::All, ind_style)?;
|
bar.add_style(Part::All, ind_style)?;
|
||||||
|
|
||||||
let mut loading_lbl = Label::new(&mut screen)?;
|
let mut loading_lbl = Label::new(&mut screen)?;
|
||||||
loading_lbl.set_text("Loading...")?;
|
loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?;
|
||||||
loading_lbl.set_align(&mut bar, Align::OutTopMid, 0, -10)?;
|
loading_lbl.set_align(&mut bar, Align::OutTopMid, 0, -10)?;
|
||||||
loading_lbl.set_label_align(LabelAlign::Center)?;
|
loading_lbl.set_label_align(LabelAlign::Center)?;
|
||||||
|
|
||||||
|
@ -75,16 +78,18 @@ fn main() -> Result<(), LvError> {
|
||||||
threaded_ui
|
threaded_ui
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.event_send(&mut loading_lbl, Event::Clicked)?
|
.event_send(&mut bar, Event::Clicked)?
|
||||||
}
|
}
|
||||||
bar.set_value(i, Animation::OFF)?;
|
bar.set_value(i, Animation::OFF)?;
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
sleep(Duration::from_millis(25));
|
sleep(Duration::from_millis(50));
|
||||||
|
|
||||||
threaded_ui.lock().unwrap().task_handler();
|
let mut ui = threaded_ui.lock().unwrap();
|
||||||
|
ui.task_handler();
|
||||||
window.update(&display);
|
if let Some(disp) = ui.get_display_ref() {
|
||||||
|
window.update(disp);
|
||||||
|
}
|
||||||
|
|
||||||
for event in window.events() {
|
for event in window.events() {
|
||||||
match event {
|
match event {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use cstr_core::CString;
|
||||||
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::{
|
||||||
|
@ -5,14 +6,14 @@ use embedded_graphics_simulator::{
|
||||||
};
|
};
|
||||||
use lvgl::style::Style;
|
use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Btn, Label};
|
use lvgl::widgets::{Btn, Label};
|
||||||
use lvgl::{self, Align, Color, DisplayDriver, Event, LvError, Part, State, Widget, UI};
|
use lvgl::{self, Align, Color, Event, LvError, Part, State, Widget, UI};
|
||||||
use lvgl_sys;
|
use lvgl_sys;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
lvgl_sys::LV_HOR_RES_MAX,
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
lvgl_sys::LV_VER_RES_MAX,
|
||||||
));
|
));
|
||||||
|
@ -23,8 +24,7 @@ fn main() -> Result<(), LvError> {
|
||||||
let mut ui = UI::init()?;
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
// Implement and register your display:
|
// Implement and register your display:
|
||||||
let display_driver = DisplayDriver::new(&mut display);
|
ui.disp_drv_register(display)?;
|
||||||
ui.disp_drv_register(display_driver);
|
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = ui.scr_act()?;
|
let mut screen = ui.scr_act()?;
|
||||||
|
@ -38,15 +38,19 @@ fn main() -> Result<(), LvError> {
|
||||||
button.set_align(&mut screen, Align::InLeftMid, 30, 0)?;
|
button.set_align(&mut screen, Align::InLeftMid, 30, 0)?;
|
||||||
button.set_size(180, 80)?;
|
button.set_size(180, 80)?;
|
||||||
let mut btn_lbl = Label::new(&mut button)?;
|
let mut btn_lbl = Label::new(&mut button)?;
|
||||||
btn_lbl.set_text("Click me!")?;
|
btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str())?;
|
||||||
|
|
||||||
let mut btn_state = false;
|
let mut btn_state = false;
|
||||||
button.on_event(|mut btn, event| {
|
button.on_event(|mut btn, event| {
|
||||||
if let lvgl::Event::Clicked = event {
|
if let lvgl::Event::Clicked = event {
|
||||||
if btn_state {
|
if btn_state {
|
||||||
btn_lbl.set_text("Click me!").unwrap();
|
btn_lbl
|
||||||
|
.set_text(CString::new("Click me!").unwrap().as_c_str())
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
btn_lbl.set_text("Clicked!").unwrap();
|
btn_lbl
|
||||||
|
.set_text(CString::new("Clicked!").unwrap().as_c_str())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
btn_state = !btn_state;
|
btn_state = !btn_state;
|
||||||
println!("Clicked!");
|
println!("Clicked!");
|
||||||
|
@ -71,9 +75,11 @@ fn main() -> Result<(), LvError> {
|
||||||
});
|
});
|
||||||
|
|
||||||
'running: loop {
|
'running: loop {
|
||||||
threaded_ui.lock().unwrap().task_handler();
|
let mut ui = threaded_ui.lock().unwrap();
|
||||||
|
ui.task_handler();
|
||||||
window.update(&display);
|
if let Some(disp) = ui.get_display_ref() {
|
||||||
|
window.update(disp);
|
||||||
|
}
|
||||||
for event in window.events() {
|
for event in window.events() {
|
||||||
match event {
|
match event {
|
||||||
SimulatorEvent::MouseButtonUp {
|
SimulatorEvent::MouseButtonUp {
|
||||||
|
@ -82,10 +88,7 @@ fn main() -> Result<(), LvError> {
|
||||||
} => {
|
} => {
|
||||||
println!("Clicked on: {:?}", point);
|
println!("Clicked on: {:?}", point);
|
||||||
// Send a event to the button directly
|
// Send a event to the button directly
|
||||||
threaded_ui
|
ui.event_send(&mut button, Event::Clicked)?;
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.event_send(&mut button, Event::Clicked)?;
|
|
||||||
}
|
}
|
||||||
SimulatorEvent::Quit => break 'running,
|
SimulatorEvent::Quit => break 'running,
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -12,9 +12,46 @@ use lvgl_sys;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
//
|
||||||
|
// struct MyApp {
|
||||||
|
// time: Label,
|
||||||
|
// bt_label: Label,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl MyApp {
|
||||||
|
// fn initialize(screen: &Obj) -> Result<Self, ()> {
|
||||||
|
// let mut screen_style = Style::default();
|
||||||
|
// screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||||
|
// screen_style.set_radius(State::DEFAULT, 0);
|
||||||
|
//
|
||||||
|
// let mut style_time = Style::default();
|
||||||
|
// style_time.set_text_color(State::DEFAULT, Color::from_rgb((255, 255, 255)));
|
||||||
|
//
|
||||||
|
// let time = screen.create_label()?;
|
||||||
|
// time.set_align(&screen, Align::Center, 0, 0)?;
|
||||||
|
// time.set_text(CString::new("20:46").unwrap().as_c_str())?;
|
||||||
|
// time.set_width(240)?;
|
||||||
|
// time.set_height(240)?;
|
||||||
|
//
|
||||||
|
// let bt = screen.create_label()?;
|
||||||
|
// bt.set_height(80)?;
|
||||||
|
// bt.set_recolor(true)?;
|
||||||
|
// bt.set_height(80)?;
|
||||||
|
// bt.set_recolor(true)?;
|
||||||
|
// bt.set_text(CString::new("#5794f2 \u{F293}#").unwrap().as_c_str())?;
|
||||||
|
// bt.set_label_align(LabelAlign::Left)?;
|
||||||
|
// bt.set_align(&screen, Align::InTopLeft, 0, 0)?;
|
||||||
|
//
|
||||||
|
// // attach styles
|
||||||
|
// screen.add_style(Part::Main, screen_style)?;
|
||||||
|
// time.add_style(Part::Main, style_time)?;
|
||||||
|
//
|
||||||
|
// Ok(MyApp { time, bt_label: bt })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
lvgl_sys::LV_HOR_RES_MAX,
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
lvgl_sys::LV_VER_RES_MAX,
|
||||||
));
|
));
|
||||||
|
@ -25,15 +62,11 @@ fn main() -> Result<(), LvError> {
|
||||||
let mut ui = UI::init()?;
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
// Implement and register your display:
|
// Implement and register your display:
|
||||||
let display_driver = lvgl::DisplayDriver::new(&mut display);
|
ui.disp_drv_register(display).unwrap();
|
||||||
ui.disp_drv_register(display_driver);
|
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = ui.scr_act()?;
|
let mut screen = ui.scr_act()?;
|
||||||
|
|
||||||
let font_roboto_28 = unsafe { &lvgl_sys::lv_theme_get_font_normal() };
|
|
||||||
let font_noto_sans_numeric_28 = unsafe { ¬o_sans_numeric_80 };
|
|
||||||
|
|
||||||
let mut screen_style = Style::default();
|
let mut screen_style = Style::default();
|
||||||
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||||
screen_style.set_radius(State::DEFAULT, 0);
|
screen_style.set_radius(State::DEFAULT, 0);
|
||||||
|
@ -68,7 +101,7 @@ fn main() -> Result<(), LvError> {
|
||||||
let mut t: heapless::String<heapless::consts::U8> = heapless::String::from("test");
|
let mut t: heapless::String<heapless::consts::U8> = heapless::String::from("test");
|
||||||
t.push('\0').unwrap();
|
t.push('\0').unwrap();
|
||||||
set_text(CStr::from_bytes_with_nul(t.as_bytes()).unwrap()).unwrap();
|
set_text(CStr::from_bytes_with_nul(t.as_bytes()).unwrap()).unwrap();
|
||||||
set_text(CStr::from_bytes_with_nul("test\0".as_bytes()).unwrap()).unwrap();
|
set_text(CStr::from_bytes_with_nul(("test\0").as_bytes()).unwrap()).unwrap();
|
||||||
set_text(cstr_core::CString::new("test").unwrap().as_c_str()).unwrap();
|
set_text(cstr_core::CString::new("test").unwrap().as_c_str()).unwrap();
|
||||||
|
|
||||||
let mut power = Label::new(&mut screen)?;
|
let mut power = Label::new(&mut screen)?;
|
||||||
|
@ -104,9 +137,11 @@ fn main() -> Result<(), LvError> {
|
||||||
|
|
||||||
sleep(Duration::from_secs(1));
|
sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
threaded_ui.lock().unwrap().task_handler();
|
let mut ui = threaded_ui.lock().unwrap();
|
||||||
|
ui.task_handler();
|
||||||
window.update(&display);
|
if let Some(disp) = ui.get_display_ref() {
|
||||||
|
window.update(disp);
|
||||||
|
}
|
||||||
|
|
||||||
for event in window.events() {
|
for event in window.events() {
|
||||||
match event {
|
match event {
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl Rusty for LvWidget {
|
||||||
|
|
||||||
fn code(&self, _parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
fn code(&self, _parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
||||||
// We don't generate for the generic Obj
|
// We don't generate for the generic Obj
|
||||||
if self.name.eq("obj") {
|
if self.name.as_str().eq("obj") {
|
||||||
return Err(WrapperError::Skip);
|
return Err(WrapperError::Skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ impl Rusty for LvFunc {
|
||||||
let original_func_name = format_ident!("{}", self.name.as_str());
|
let original_func_name = format_ident!("{}", self.name.as_str());
|
||||||
|
|
||||||
// generate constructor
|
// generate constructor
|
||||||
if new_name.eq("create") {
|
if new_name.as_str().eq("create") {
|
||||||
return Ok(quote! {
|
return Ok(quote! {
|
||||||
pub fn new<C>(parent: &mut C) -> crate::LvResult<Self>
|
pub fn new<C>(parent: &mut C) -> crate::LvResult<Self>
|
||||||
where
|
where
|
||||||
|
@ -105,9 +105,12 @@ impl Rusty for LvFunc {
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = lvgl_sys::#original_func_name(parent.raw()?.as_mut(), core::ptr::null_mut());
|
let ptr = lvgl_sys::#original_func_name(parent.raw()?.as_mut(), core::ptr::null_mut());
|
||||||
let raw = core::ptr::NonNull::new(ptr)?;
|
if let Some(raw) = core::ptr::NonNull::new(ptr) {
|
||||||
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
||||||
Ok(Self { core })
|
Ok(Self { core })
|
||||||
|
} else {
|
||||||
|
Err(crate::LvError::InvalidReference)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -631,11 +634,15 @@ mod test {
|
||||||
where
|
where
|
||||||
C: crate::NativeObject,
|
C: crate::NativeObject,
|
||||||
{
|
{
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = lvgl_sys::lv_arc_create(parent.raw()?.as_mut(), core::ptr::null_mut());
|
let ptr = lvgl_sys::lv_arc_create(parent.raw()?.as_mut(), core::ptr::null_mut());
|
||||||
let raw = core::ptr::NonNull::new(ptr)?;
|
if let Some(raw) = core::ptr::NonNull::new(ptr) {
|
||||||
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
||||||
Ok(Self { core })
|
Ok(Self { core })
|
||||||
|
} else {
|
||||||
|
Err(crate::LvError::InvalidReference)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
use crate::Color;
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use embedded_graphics::prelude::*;
|
|
||||||
use embedded_graphics::{drawable, DrawTarget};
|
|
||||||
|
|
||||||
pub struct DisplayDriver {
|
|
||||||
pub(crate) raw: lvgl_sys::lv_disp_drv_t,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisplayDriver {
|
|
||||||
// we should accept a Rc<RefCell<T>> and throw it in a box and add to the user_data of the callback handler function
|
|
||||||
pub fn new<T, C>(device: &mut T) -> Self
|
|
||||||
where
|
|
||||||
T: DrawTarget<C>,
|
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
|
||||||
let disp_drv = unsafe {
|
|
||||||
// Declare a buffer for the refresh rate
|
|
||||||
// TODO: Make this an external configuration
|
|
||||||
const REFRESH_BUFFER_LEN: usize = 2;
|
|
||||||
const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
|
|
||||||
|
|
||||||
let refresh_buffer1: [lvgl_sys::lv_color_t; BUF_SIZE] =
|
|
||||||
[Color::from_rgb((0, 0, 0)).raw; BUF_SIZE];
|
|
||||||
|
|
||||||
let refresh_buffer2: [lvgl_sys::lv_color_t; BUF_SIZE] =
|
|
||||||
[Color::from_rgb((0, 0, 0)).raw; BUF_SIZE];
|
|
||||||
|
|
||||||
// Create a display buffer for LittlevGL
|
|
||||||
let mut display_buffer = MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit();
|
|
||||||
// Initialize the display buffer
|
|
||||||
lvgl_sys::lv_disp_buf_init(
|
|
||||||
display_buffer.as_mut_ptr(),
|
|
||||||
Box::into_raw(Box::new(refresh_buffer1)) as *mut cty::c_void,
|
|
||||||
Box::into_raw(Box::new(refresh_buffer2)) as *mut cty::c_void,
|
|
||||||
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
|
||||||
);
|
|
||||||
let display_buffer = Box::new(display_buffer.assume_init());
|
|
||||||
|
|
||||||
// Basic initialization of the display driver
|
|
||||||
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit();
|
|
||||||
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
|
|
||||||
let mut disp_drv = disp_drv.assume_init();
|
|
||||||
// Assign the buffer to the display
|
|
||||||
disp_drv.buffer = Box::into_raw(display_buffer);
|
|
||||||
// Set your driver function
|
|
||||||
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>);
|
|
||||||
// TODO: DrawHandler type here
|
|
||||||
// Safety: `user_data` is set to NULL in C code.
|
|
||||||
disp_drv.user_data = device as *mut _ as *mut cty::c_void;
|
|
||||||
disp_drv
|
|
||||||
};
|
|
||||||
Self { raw: disp_drv }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to keep a reference to the DisplayDriver in UI if we implement Drop
|
|
||||||
// impl Drop for DisplayDriver {
|
|
||||||
// fn drop(&mut self) {
|
|
||||||
// // grab the user data and deref the DrawHandler to free the instance for dealloc in the Rust universe.
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// a reference is kept to the external drawing target (T)
|
|
||||||
// the reference is kept in the callback function of the drawing handler
|
|
||||||
// we need a reference counter for the drawing target and free the ref counter when the display is
|
|
||||||
// destroyed.
|
|
||||||
//type DrawHandler = Rc<RefCell<u8>>;
|
|
||||||
//
|
|
||||||
// impl Drop for DrawHandler {
|
|
||||||
// fn drop(&mut self) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
unsafe extern "C" fn display_callback_wrapper<T, C>(
|
|
||||||
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
|
|
||||||
T: DrawTarget<C>,
|
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
|
||||||
// In the `std` world we would make sure to capture panics here and make them not escape across
|
|
||||||
// the FFI boundary. Since this library is focused on embedded platforms, we don't
|
|
||||||
// have an standard unwinding mechanism to rely upon.
|
|
||||||
let display_driver = *disp_drv;
|
|
||||||
// Rust code closure reference
|
|
||||||
if !display_driver.user_data.is_null() {
|
|
||||||
let device = &mut *(display_driver.user_data as *mut T);
|
|
||||||
let x1 = (*area).x1;
|
|
||||||
let x2 = (*area).x2;
|
|
||||||
let y1 = (*area).y1;
|
|
||||||
let y2 = (*area).y2;
|
|
||||||
// TODO: Can we do anything when there is a error while flushing?
|
|
||||||
let _ = display_flush(device, (x1, x2), (y1, y2), color_p);
|
|
||||||
}
|
|
||||||
// Indicate to LittlevGL that we are ready with the flushing
|
|
||||||
lvgl_sys::lv_disp_flush_ready(disp_drv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We separate this display flush function to reduce the amount of unsafe code we need to write.
|
|
||||||
// This also provides a good separation of concerns, what is necessary from LittlevGL to work and
|
|
||||||
// what is the lvgl-rs wrapper responsibility.
|
|
||||||
fn display_flush<T, C>(
|
|
||||||
display: &mut T,
|
|
||||||
(x1, x2): (i16, i16),
|
|
||||||
(y1, y2): (i16, i16),
|
|
||||||
color_p: *mut lvgl_sys::lv_color_t,
|
|
||||||
) -> Result<(), T::Error>
|
|
||||||
where
|
|
||||||
T: DrawTarget<C>,
|
|
||||||
C: PixelColor + From<Color>,
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
Ok(display.draw_iter(pixels)?)
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
use crate::{DisplayDriver, Event, LvError, LvResult, Obj, Widget};
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::ptr;
|
|
||||||
use core::ptr::NonNull;
|
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use core::time::Duration;
|
|
||||||
|
|
||||||
// There can only be a single reference to LittlevGL library.
|
|
||||||
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
pub struct UI {
|
|
||||||
// LittlevGL is not thread-safe by default.
|
|
||||||
_not_sync: PhantomData<*mut ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// LittlevGL does not use thread locals.
|
|
||||||
unsafe impl Send for UI {}
|
|
||||||
|
|
||||||
impl UI {
|
|
||||||
pub fn init() -> LvResult<Self> {
|
|
||||||
if !LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) {
|
|
||||||
unsafe {
|
|
||||||
lvgl_sys::lv_init();
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
_not_sync: PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(LvError::AlreadyInUse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disp_drv_register(&mut self, display: DisplayDriver) {
|
|
||||||
// Throw display driver into a box and add to user data (if we need to get the display back)
|
|
||||||
// or simply forget the display pointer/object to prevent Drop to be called
|
|
||||||
// register it
|
|
||||||
unsafe {
|
|
||||||
let boxed = Box::new(display.raw);
|
|
||||||
lvgl_sys::lv_disp_drv_register(Box::into_raw(boxed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scr_act(&self) -> LvResult<Obj> {
|
|
||||||
unsafe {
|
|
||||||
let screen = lvgl_sys::lv_disp_get_scr_act(ptr::null_mut());
|
|
||||||
Ok(Obj::from_raw(NonNull::new(screen)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn event_send<T>(&mut self, obj: &mut T, event: Event<T::SpecialEvent>) -> LvResult<()>
|
|
||||||
where
|
|
||||||
T: Widget,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
lvgl_sys::lv_event_send(obj.raw()?.as_mut(), event.into(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick_inc(&mut self, tick_period: Duration) {
|
|
||||||
unsafe {
|
|
||||||
lvgl_sys::lv_tick_inc(tick_period.as_millis() as u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn task_handler(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
lvgl_sys::lv_task_handler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +1,15 @@
|
||||||
#![feature(try_trait)]
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate alloc;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
mod display;
|
pub mod mem;
|
||||||
mod global;
|
|
||||||
mod support;
|
mod support;
|
||||||
|
mod ui;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod lv_core;
|
mod lv_core;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
pub use display::DisplayDriver;
|
|
||||||
pub use global::UI;
|
|
||||||
pub use lv_core::*;
|
pub use lv_core::*;
|
||||||
pub use support::*;
|
pub use support::*;
|
||||||
|
pub use ui::*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::lv_core::style::Style;
|
use crate::lv_core::style::Style;
|
||||||
|
use crate::mem::Box;
|
||||||
use crate::{Align, LvError, LvResult};
|
use crate::{Align, LvError, LvResult};
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
/// Represents a native LittlevGL object
|
/// Represents a native LittlevGL object
|
||||||
|
@ -40,7 +40,7 @@ pub trait Widget: NativeObject {
|
||||||
///
|
///
|
||||||
unsafe fn from_raw(raw_pointer: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self;
|
unsafe fn from_raw(raw_pointer: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self;
|
||||||
|
|
||||||
fn add_style(&mut self, part: Self::Part, style: Style) -> LvResult<()> {
|
fn add_style(&self, part: Self::Part, style: Style) -> LvResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
lvgl_sys::lv_obj_add_style(self.raw()?.as_mut(), part.into(), Box::into_raw(style.raw));
|
lvgl_sys::lv_obj_add_style(self.raw()?.as_mut(), part.into(), Box::into_raw(style.raw));
|
||||||
};
|
};
|
||||||
|
@ -146,8 +146,8 @@ macro_rules! define_object {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut raw = self.raw()?;
|
let mut raw = self.raw()?;
|
||||||
let obj = raw.as_mut();
|
let obj = raw.as_mut();
|
||||||
let user_closure = alloc::boxed::Box::new(f);
|
let user_closure = $crate::mem::Box::new(f)?;
|
||||||
obj.user_data = alloc::boxed::Box::into_raw(user_closure) as *mut cty::c_void;
|
obj.user_data = $crate::mem::Box::into_raw(user_closure) as *mut cty::c_void;
|
||||||
lvgl_sys::lv_obj_set_event_cb(
|
lvgl_sys::lv_obj_set_event_cb(
|
||||||
obj,
|
obj,
|
||||||
lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::<Self, F>),
|
lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::<Self, F>),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
use crate::mem::Box;
|
||||||
use crate::{Color, LvResult, State};
|
use crate::{Color, LvResult, State};
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use cstr_core::CString;
|
use cstr_core::CStr;
|
||||||
|
|
||||||
pub enum Themes {
|
pub enum Themes {
|
||||||
Pretty,
|
Pretty,
|
||||||
|
@ -12,15 +12,14 @@ pub struct Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn set_value_str(&mut self, state: State, value: &str) -> LvResult<()> {
|
pub fn set_value_str(&mut self, state: State, value: &CStr) -> LvResult<()> {
|
||||||
let native_state: u32 = state.get_bits();
|
let native_state: u32 = state.get_bits();
|
||||||
let string = CString::new(value)?;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
lvgl_sys::_lv_style_set_ptr(
|
lvgl_sys::_lv_style_set_ptr(
|
||||||
self.raw.as_mut(),
|
self.raw.as_mut(),
|
||||||
(lvgl_sys::LV_STYLE_VALUE_STR
|
(lvgl_sys::LV_STYLE_VALUE_STR
|
||||||
| (native_state << lvgl_sys::LV_STYLE_STATE_POS as u32)) as u16,
|
| (native_state << lvgl_sys::LV_STYLE_STATE_POS as u32)) as u16,
|
||||||
string.into_raw() as *mut cty::c_void,
|
value.as_ptr() as *mut cty::c_void,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -32,7 +31,7 @@ impl Default for Style {
|
||||||
let raw = unsafe {
|
let raw = unsafe {
|
||||||
let mut style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
let mut style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
||||||
lvgl_sys::lv_style_init(style.as_mut_ptr());
|
lvgl_sys::lv_style_init(style.as_mut_ptr());
|
||||||
Box::new(style.assume_init())
|
Box::new(style.assume_init()).unwrap()
|
||||||
};
|
};
|
||||||
Self { raw }
|
Self { raw }
|
||||||
}
|
}
|
||||||
|
|
100
lvgl/src/mem.rs
Normal file
100
lvgl/src/mem.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use crate::{LvError, LvResult};
|
||||||
|
use core::mem;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use core::ptr;
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
/// Places `T` into LVGL memory.
|
||||||
|
pub struct Box<T: Sized>(NonNull<T>);
|
||||||
|
|
||||||
|
impl<T: Sized> Box<T> {
|
||||||
|
pub fn new(inner: T) -> LvResult<Box<T>> {
|
||||||
|
let layout = mem::size_of::<T>();
|
||||||
|
let inner = unsafe {
|
||||||
|
let ptr = lvgl_sys::lv_mem_alloc(layout as lvgl_sys::size_t) as *mut T;
|
||||||
|
match NonNull::new(ptr) {
|
||||||
|
Some(v) => {
|
||||||
|
// Place value in new mem
|
||||||
|
ptr::write(ptr, inner);
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
None => Err(LvError::LvOOMemory),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Box(inner?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_raw(b: Box<T>) -> *mut T {
|
||||||
|
let b = mem::ManuallyDrop::new(b);
|
||||||
|
b.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sized> Drop for Box<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_mem_free(self.0.as_ptr() as *const cty::c_void);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sized> Deref for Box<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe { self.0.as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sized> DerefMut for Box<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
unsafe { self.0.as_mut() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sized> AsMut<T> for Box<T> {
|
||||||
|
fn as_mut(&mut self) -> &mut T {
|
||||||
|
unsafe { self.0.as_mut() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn place_value_in_lv_mem() {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::_lv_mem_init();
|
||||||
|
};
|
||||||
|
let v = Box::new(5).unwrap();
|
||||||
|
drop(v);
|
||||||
|
let v = Box::new(10).unwrap();
|
||||||
|
drop(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn place_complex_value_in_lv_mem() {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::_lv_mem_init();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
disp: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = Point {
|
||||||
|
x: 32,
|
||||||
|
y: 240,
|
||||||
|
disp: -100,
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = Box::new(p).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(b.x, 32);
|
||||||
|
assert_eq!(b.y, 240);
|
||||||
|
assert_eq!(b.disp, -100);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::Widget;
|
use crate::Widget;
|
||||||
use bitflags::_core::option::NoneError;
|
|
||||||
use core::convert::{TryFrom, TryInto};
|
use core::convert::{TryFrom, TryInto};
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use cstr_core::NulError;
|
|
||||||
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
||||||
|
|
||||||
pub type LvResult<T> = Result<T, LvError>;
|
pub type LvResult<T> = Result<T, LvError>;
|
||||||
|
@ -11,24 +9,10 @@ pub type LvResult<T> = Result<T, LvError>;
|
||||||
pub enum LvError {
|
pub enum LvError {
|
||||||
InvalidReference,
|
InvalidReference,
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
InvalidNulByteString,
|
LvOOMemory,
|
||||||
StringSizeTooShort,
|
|
||||||
StringCannotAppendNulByte,
|
|
||||||
AlreadyInUse,
|
AlreadyInUse,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NoneError> for LvError {
|
|
||||||
fn from(_: NoneError) -> Self {
|
|
||||||
LvError::InvalidReference
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NulError> for LvError {
|
|
||||||
fn from(_: NulError) -> Self {
|
|
||||||
LvError::InvalidNulByteString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
pub(crate) raw: lvgl_sys::lv_color_t,
|
pub(crate) raw: lvgl_sys::lv_color_t,
|
||||||
|
|
222
lvgl/src/ui.rs
Normal file
222
lvgl/src/ui.rs
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::mem;
|
||||||
|
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||||
|
use core::ptr;
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use core::time::Duration;
|
||||||
|
use embedded_graphics::pixelcolor::PixelColor;
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
use embedded_graphics::{drawable, DrawTarget};
|
||||||
|
|
||||||
|
// There can only be a single reference to LittlevGL library.
|
||||||
|
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
// TODO: Make this an external configuration
|
||||||
|
const REFRESH_BUFFER_LEN: usize = 2;
|
||||||
|
// Declare a buffer for the refresh rate
|
||||||
|
const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
|
||||||
|
|
||||||
|
type RefreshBuffer = [lvgl_sys::lv_color_t; BUF_SIZE];
|
||||||
|
|
||||||
|
pub struct UI<T, C>
|
||||||
|
where
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
// LittlevGL is not thread-safe by default.
|
||||||
|
_not_sync: PhantomData<*mut ()>,
|
||||||
|
// Later we can add possibility to have multiple displays by using `heapless::Vec`
|
||||||
|
display_data: Option<DisplayUserData<T, C>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LittlevGL does not use thread locals.
|
||||||
|
unsafe impl<T, C> Send for UI<T, C>
|
||||||
|
where
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, C> UI<T, C>
|
||||||
|
where
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
pub fn init() -> LvResult<Self> {
|
||||||
|
if !LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_init();
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
_not_sync: PhantomData,
|
||||||
|
display_data: None,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(LvError::AlreadyInUse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disp_drv_register(&mut self, display: T) -> LvResult<()> {
|
||||||
|
self.display_data = Some(DisplayUserData {
|
||||||
|
display,
|
||||||
|
phantom: PhantomData,
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Create a display buffer for LittlevGL
|
||||||
|
// Never initialized in Rust side (don't call `assume_init`, this is C managed memory)!
|
||||||
|
let disp_buf = lvgl_sys::lv_mem_alloc(
|
||||||
|
mem::size_of::<lvgl_sys::lv_disp_buf_t>() as lvgl_sys::size_t
|
||||||
|
) as *mut lvgl_sys::lv_disp_buf_t;
|
||||||
|
// Initialize the display buffer
|
||||||
|
let buffer_size = mem::size_of::<RefreshBuffer>();
|
||||||
|
let buf1 = lvgl_sys::lv_mem_alloc(buffer_size as lvgl_sys::size_t);
|
||||||
|
if buf1.is_null() {
|
||||||
|
lvgl_sys::lv_mem_free(disp_buf as *mut cty::c_void);
|
||||||
|
return Err(LvError::LvOOMemory);
|
||||||
|
}
|
||||||
|
let buf2 = lvgl_sys::lv_mem_alloc(buffer_size as lvgl_sys::size_t);
|
||||||
|
if buf2.is_null() {
|
||||||
|
lvgl_sys::lv_mem_free(disp_buf as *mut cty::c_void);
|
||||||
|
lvgl_sys::lv_mem_free(buf1);
|
||||||
|
return Err(LvError::LvOOMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
lvgl_sys::lv_disp_buf_init(
|
||||||
|
disp_buf,
|
||||||
|
buf1,
|
||||||
|
buf2,
|
||||||
|
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Basic initialization of the display driver
|
||||||
|
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit();
|
||||||
|
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
|
||||||
|
// Since this is C managed memory, we don't want to drop it using Rust, thus `ManuallyDrop` wrapping.
|
||||||
|
let mut disp_drv = ManuallyDrop::new(disp_drv.assume_init());
|
||||||
|
// Assign the buffer to the display
|
||||||
|
disp_drv.buffer = disp_buf;
|
||||||
|
// Set your driver function
|
||||||
|
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>);
|
||||||
|
// TODO: DrawHandler type here
|
||||||
|
// Safety: `user_data` is set to NULL in C code.
|
||||||
|
disp_drv.user_data = &mut self.display_data as *mut _ as *mut cty::c_void;
|
||||||
|
lvgl_sys::lv_disp_drv_register(
|
||||||
|
&mut ManuallyDrop::take(&mut disp_drv) as *mut lvgl_sys::lv_disp_drv_t
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_display_ref(&self) -> Option<&T> {
|
||||||
|
match self.display_data.as_ref() {
|
||||||
|
None => None,
|
||||||
|
Some(v) => Some(&v.display),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scr_act(&self) -> LvResult<Obj> {
|
||||||
|
unsafe {
|
||||||
|
let screen = lvgl_sys::lv_disp_get_scr_act(ptr::null_mut());
|
||||||
|
if let Some(v) = NonNull::new(screen) {
|
||||||
|
Ok(Obj::from_raw(v))
|
||||||
|
} else {
|
||||||
|
Err(LvError::InvalidReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_send<W>(&mut self, obj: &mut W, event: Event<W::SpecialEvent>) -> LvResult<()>
|
||||||
|
where
|
||||||
|
W: Widget,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_event_send(obj.raw()?.as_mut(), event.into(), ptr::null_mut());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick_inc(&mut self, tick_period: Duration) {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_tick_inc(tick_period.as_millis() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn task_handler(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_task_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct DisplayUserData<T, C>
|
||||||
|
where
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
display: T,
|
||||||
|
phantom: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn display_callback_wrapper<T, C>(
|
||||||
|
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
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
// In the `std` world we would make sure to capture panics here and make them not escape across
|
||||||
|
// the FFI boundary. Since this library is focused on embedded platforms, we don't
|
||||||
|
// have an standard unwinding mechanism to rely upon.
|
||||||
|
let display_driver = *disp_drv;
|
||||||
|
// Rust code closure reference
|
||||||
|
if !display_driver.user_data.is_null() {
|
||||||
|
let user_data = &mut *(display_driver.user_data as *mut DisplayUserData<T, C>);
|
||||||
|
let x1 = (*area).x1;
|
||||||
|
let x2 = (*area).x2;
|
||||||
|
let y1 = (*area).y1;
|
||||||
|
let y2 = (*area).y2;
|
||||||
|
// TODO: Can we do anything when there is a error while flushing?
|
||||||
|
let _ = display_flush(&mut user_data.display, (x1, x2), (y1, y2), color_p);
|
||||||
|
}
|
||||||
|
// Indicate to LittlevGL that we are ready with the flushing
|
||||||
|
lvgl_sys::lv_disp_flush_ready(disp_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We separate this display flush function to reduce the amount of unsafe code we need to write.
|
||||||
|
// This also provides a good separation of concerns, what is necessary from LittlevGL to work and
|
||||||
|
// what is the lvgl-rs wrapper responsibility.
|
||||||
|
fn display_flush<T, C>(
|
||||||
|
display: &mut T,
|
||||||
|
(x1, x2): (i16, i16),
|
||||||
|
(y1, y2): (i16, i16),
|
||||||
|
color_p: *mut lvgl_sys::lv_color_t,
|
||||||
|
) -> Result<(), T::Error>
|
||||||
|
where
|
||||||
|
T: DrawTarget<C>,
|
||||||
|
C: PixelColor + From<Color>,
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
Ok(display.draw_iter(pixels)?)
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ mod label;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
|
|
||||||
use crate::{NativeObject, Widget};
|
use crate::NativeObject;
|
||||||
pub use arc::*;
|
pub use arc::*;
|
||||||
pub use bar::*;
|
pub use bar::*;
|
||||||
pub use gauge::*;
|
pub use gauge::*;
|
||||||
|
|
Loading…
Reference in a new issue