API re-design #9
7 changed files with 214 additions and 348 deletions
|
@ -1,13 +1,14 @@
|
|||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl;
|
||||
use lvgl::Object;
|
||||
use lvgl::{Object, UI};
|
||||
use lvgl_sys;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use embedded_graphics::pixelcolor::{Rgb565};
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
||||
|
@ -18,15 +19,14 @@ fn main() -> Result<(), String> {
|
|||
let output_settings = OutputSettingsBuilder::new().scale(4).build();
|
||||
let mut window = Window::new("Hello World", &output_settings);
|
||||
|
||||
unsafe {
|
||||
lvgl_sys::lv_init();
|
||||
}
|
||||
let mut ui = UI::init().unwrap();
|
||||
|
||||
// Implement and register your display:
|
||||
let mut display_driver = lvgl::DisplayDriver::new(&mut display);
|
||||
let display_driver = lvgl::DisplayDriver::new(&mut display);
|
||||
ui.disp_drv_register(display_driver);
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = display_driver.get_active_screen();
|
||||
let mut screen = ui.scr_act();
|
||||
|
||||
let font_roboto_28 = unsafe { &lvgl_sys::lv_font_roboto_28 };
|
||||
let font_noto_sans_numeric_28 = unsafe { ¬o_sans_numeric_80 };
|
||||
|
@ -34,13 +34,13 @@ fn main() -> Result<(), String> {
|
|||
let mut screen_style = lvgl::Style::new();
|
||||
screen_style.set_body_main_color(lvgl::Color::from_rgb((0, 0, 0)));
|
||||
screen_style.set_body_grad_color(lvgl::Color::from_rgb((0, 0, 0)));
|
||||
screen.set_style(&mut screen_style);
|
||||
screen.set_style(screen_style);
|
||||
|
||||
let mut time = lvgl::Label::new(&mut screen);
|
||||
let mut style_time = lvgl::Style::new();
|
||||
style_time.set_text_font(font_noto_sans_numeric_28);
|
||||
style_time.set_text_color(lvgl::Color::from_rgb((255, 255, 255)));
|
||||
time.set_style(&mut style_time);
|
||||
time.set_style(style_time);
|
||||
time.set_align(&mut screen, lvgl::Align::InLeftMid, 20, 0);
|
||||
time.set_text("20:46\0");
|
||||
time.set_width(240);
|
||||
|
@ -49,8 +49,8 @@ fn main() -> Result<(), String> {
|
|||
let mut bt = lvgl::Label::new(&mut screen);
|
||||
let mut style_bt = lvgl::Style::new();
|
||||
style_bt.set_text_font(font_roboto_28);
|
||||
let mut style_power = style_bt.clone();
|
||||
bt.set_style(&mut style_bt);
|
||||
let style_power = style_bt.clone();
|
||||
bt.set_style(style_bt);
|
||||
bt.set_width(50);
|
||||
bt.set_height(80);
|
||||
bt.set_recolor(true);
|
||||
|
@ -59,7 +59,7 @@ fn main() -> Result<(), String> {
|
|||
bt.set_align(&mut screen, lvgl::Align::InTopLeft, 0, 0);
|
||||
|
||||
let mut power = lvgl::Label::new(&mut screen);
|
||||
power.set_style(&mut style_power);
|
||||
power.set_style(style_power);
|
||||
power.set_recolor(true);
|
||||
power.set_width(80);
|
||||
power.set_height(20);
|
||||
|
@ -67,12 +67,15 @@ fn main() -> Result<(), String> {
|
|||
power.set_label_align(lvgl::LabelAlign::Right);
|
||||
power.set_align(&mut screen, lvgl::Align::InTopRight, 0, 0);
|
||||
|
||||
let threaded_ui = Arc::new(Mutex::new(ui));
|
||||
|
||||
let (stop_ch, read_ch) = mpsc::channel();
|
||||
let closure_ui = threaded_ui.clone();
|
||||
let tick_thr = std::thread::spawn(move || loop {
|
||||
::std::thread::sleep(Duration::from_millis(5));
|
||||
unsafe {
|
||||
lvgl_sys::lv_tick_inc(5);
|
||||
}
|
||||
let period = Duration::from_millis(5);
|
||||
closure_ui.lock().unwrap().tick_inc(period);
|
||||
|
||||
sleep(period);
|
||||
if read_ch.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
|
@ -86,13 +89,11 @@ fn main() -> Result<(), String> {
|
|||
time.set_text(format!("21:{:02}\0", i).as_str());
|
||||
i = 1 + i;
|
||||
|
||||
::std::thread::sleep(Duration::from_millis(
|
||||
sleep(Duration::from_millis(
|
||||
lvgl_sys::LV_DISP_DEF_REFR_PERIOD as u64,
|
||||
));
|
||||
|
||||
unsafe {
|
||||
lvgl_sys::lv_task_handler();
|
||||
}
|
||||
threaded_ui.lock().unwrap().task_handler();
|
||||
|
||||
window.update(&display);
|
||||
|
||||
|
|
211
lvgl/src/api.rs
211
lvgl/src/api.rs
|
@ -1,211 +0,0 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::time::Duration;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::cell::{RefCell, RefMut};
|
||||
|
||||
// There can only be a single reference to LittlevGL library.
|
||||
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub enum LvError {
|
||||
AlreadyInUse,
|
||||
}
|
||||
|
||||
pub struct UI {
|
||||
// LittlevGL is not thread-safe by default.
|
||||
_not_send: PhantomData<*const ()>,
|
||||
display: Option<DisplayDriver>,
|
||||
}
|
||||
|
||||
impl UI {
|
||||
pub fn init() -> Result<Self, LvError> {
|
||||
if LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) == false {
|
||||
// lvgl_sys::lv_init();
|
||||
Ok(Self {
|
||||
_not_send: PhantomData,
|
||||
display: None,
|
||||
})
|
||||
} else {
|
||||
Err(LvError::AlreadyInUse)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disp_drv_register(&mut self, display: DisplayDriver) {
|
||||
// register it
|
||||
// lvgl_sys::lv_disp_drv_register(&mut disp_drv);
|
||||
self.display = Some(display);
|
||||
}
|
||||
|
||||
pub fn scr_act(&self) -> Option<&Screen> {
|
||||
match self.display.as_ref() {
|
||||
Some(drv) => {
|
||||
Some(drv.scr_act())
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick_inc(&mut self, _tick_period: Duration) {
|
||||
// lvgl_sys::lv_tick_inc(tick_period);
|
||||
}
|
||||
|
||||
pub fn task_handler(&mut self) {
|
||||
// lvgl_sys::lv_task_handler();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayDriver {
|
||||
refresh_lines: u32,
|
||||
current_screen: Screen,
|
||||
}
|
||||
|
||||
impl DisplayDriver {
|
||||
pub fn new(refresh_lines: u32) -> Self {
|
||||
Self{ refresh_lines, current_screen: Screen::new() }
|
||||
}
|
||||
|
||||
// Ensure cannot delete the current loaded screen
|
||||
pub fn load_scr(&mut self, screen: Screen) {
|
||||
self.current_screen = screen;
|
||||
}
|
||||
|
||||
pub fn scr_act(&self) -> &Screen {
|
||||
&self.current_screen
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LvObject {
|
||||
fn label_create(&self) -> Label where Self: Sized {
|
||||
Label{ parent: self }
|
||||
}
|
||||
|
||||
fn btn_create<F>(&self) -> Button<F> where Self: Sized, F: FnMut() {
|
||||
Button{ parent: self, event_callback: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Screen {
|
||||
_not_send: PhantomData<*const ()>
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
pub fn new() -> Self {
|
||||
Self { _not_send: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl LvObject for Screen {}
|
||||
|
||||
pub struct Label<'a> {
|
||||
parent: &'a dyn LvObject,
|
||||
}
|
||||
|
||||
impl<'a> Label<'a> {
|
||||
pub fn set_text(&mut self, _text: &str) {
|
||||
// set text call to unsafe...
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LvObject for Label<'a> {}
|
||||
|
||||
pub struct Button<'a, F> where F: FnMut() {
|
||||
parent: &'a dyn LvObject,
|
||||
event_callback: Option<F>,
|
||||
}
|
||||
|
||||
impl<'a, F> Button<'a, F> where F: FnMut() {
|
||||
pub fn on_event(&mut self, callback: F) {
|
||||
// add callback
|
||||
self.event_callback = Some(callback);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F> LvObject for Button<'a, F> where F: FnMut() {}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::api::{UI, DisplayDriver, LvObject};
|
||||
use core::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn basic_usage() {
|
||||
let mut ui = UI::init().unwrap();
|
||||
|
||||
let refresh_lines = 10;
|
||||
let display = DisplayDriver::new(refresh_lines);
|
||||
ui.disp_drv_register(display);
|
||||
|
||||
{
|
||||
let screen = ui.scr_act().unwrap();
|
||||
let mut button = screen.btn_create();
|
||||
|
||||
button.on_event(|| {
|
||||
// something
|
||||
let screen = ui.scr_act().unwrap();
|
||||
let mut label = screen.label_create();
|
||||
label.set_text("Clicked");
|
||||
});
|
||||
|
||||
let mut label = button.label_create();
|
||||
label.set_text("Click me!");
|
||||
};
|
||||
|
||||
{
|
||||
let screen = ui.scr_act().unwrap();
|
||||
let mut button2 = screen.btn_create();
|
||||
button2.on_event(|| {
|
||||
// else
|
||||
});
|
||||
let mut label2 = button2.label_create();
|
||||
label2.set_text("Else");
|
||||
};
|
||||
|
||||
ui.tick_inc(Duration::from_millis(5));
|
||||
ui.task_handler();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_usage() {
|
||||
// // can use Arc<Mutex<lvgl::App>> to share between threads
|
||||
// let mut app = lvgl::App::init().unwrap();
|
||||
//
|
||||
// let disp = DisplayDriver::new();
|
||||
// app.register_display_driver(&disp); // takes (&mut self, ...)
|
||||
//
|
||||
// let screen: &mut lvgl::Screen = disp.new_screen(); // takes (&mut self)
|
||||
// disp.load_screen(&screen); // takes (&self) because it just need the ref to
|
||||
// // the screen to load, as all the screens are already internal.
|
||||
//
|
||||
// let button = screen.new_button(); // takes (&self)
|
||||
// button.on_event(|&mut app, &mut btn, ev| {
|
||||
// let mut label: &mut Label = app.cur_screen().new_label();
|
||||
// if let lvgl::Event::Clicked = ev {
|
||||
// btn.set_text("clicked!");
|
||||
// }
|
||||
// app.load_screen(&screen);
|
||||
// });
|
||||
//
|
||||
// let mut lbl_click = button.new_label();
|
||||
// lbl_click.set_text("Click me!");
|
||||
//
|
||||
//
|
||||
// // lvgl::Timer returns reference to same internal object
|
||||
// app.tick(Duration::from_millis(1)); // takes (&mut self)
|
||||
//
|
||||
// app.task_handler(); // takes (&mut self)
|
||||
//
|
||||
// // Multiple Displays can be instantiated
|
||||
// // Multiple Screen's in each Display
|
||||
// // Screen can be instantiated independently
|
||||
// // Any specific object impl Screenable(trait) (can be used anywhere Screen is used)
|
||||
// // To get active screen from a Display use `lv_disp_get_scr_act(disp)`
|
||||
// let screen = disp.get_active_screen();
|
||||
// // To set an active screen in a Display use `lv_disp_load_scr(disp, scr)`
|
||||
// disp.load_screen(screen);
|
||||
//
|
||||
// let mut screen = Screen::new();
|
||||
// let mut button = screen.add_button();
|
||||
// let mut btn_lbl = button.create_label();
|
||||
}
|
||||
}
|
|
@ -1,105 +1,84 @@
|
|||
use crate::objx::ObjectX;
|
||||
use core::marker::PhantomData;
|
||||
use crate::Color;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr;
|
||||
use embedded_graphics;
|
||||
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::{drawable, DrawTarget};
|
||||
use lvgl_sys::lv_color_t;
|
||||
|
||||
pub struct DisplayDriver<'a, T, C>
|
||||
where
|
||||
T: DrawTarget<C>,
|
||||
C: PixelColor + From<ColorRgb>
|
||||
{
|
||||
raw: lvgl_sys::lv_disp_drv_t,
|
||||
display_buffer: MaybeUninit<lvgl_sys::lv_disp_buf_t>,
|
||||
refresh_buffer: [MaybeUninit<lvgl_sys::lv_color_t>; lvgl_sys::LV_HOR_RES_MAX as usize * 10],
|
||||
phantom: &'a PhantomData<T>,
|
||||
phantom2: PhantomData<C>,
|
||||
pub struct DisplayDriver {
|
||||
pub(crate) raw: lvgl_sys::lv_disp_drv_t,
|
||||
}
|
||||
|
||||
impl<'a, T, C> DisplayDriver<'a, T, C>
|
||||
where
|
||||
T: DrawTarget<C>,
|
||||
C: PixelColor + From<ColorRgb>
|
||||
{
|
||||
pub fn new(device: &'a mut T) -> Self {
|
||||
// Create a display buffer for LittlevGL
|
||||
let mut display_buffer = MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit();
|
||||
// Declare a buffer for 10 lines
|
||||
let mut refresh_buffer: [MaybeUninit<lvgl_sys::lv_color_t>;
|
||||
lvgl_sys::LV_HOR_RES_MAX as usize * 10] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
// Initialize the display buffer
|
||||
unsafe {
|
||||
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 {
|
||||
// Create a display buffer for LittlevGL
|
||||
let mut display_buffer =
|
||||
Box::new(MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit().assume_init());
|
||||
|
||||
// Declare a buffer for the refresh rate
|
||||
let refresh_buffer = Box::new(
|
||||
MaybeUninit::<
|
||||
[MaybeUninit<lvgl_sys::lv_color_t>; lvgl_sys::LV_HOR_RES_MAX as usize * 10],
|
||||
>::uninit()
|
||||
.assume_init(),
|
||||
);
|
||||
|
||||
// Initialize the display buffer
|
||||
lvgl_sys::lv_disp_buf_init(
|
||||
display_buffer.as_mut_ptr(),
|
||||
refresh_buffer.as_mut_ptr() as *mut cty::c_void,
|
||||
display_buffer.as_mut(),
|
||||
Box::into_raw(refresh_buffer) as *mut cty::c_void,
|
||||
core::ptr::null_mut(),
|
||||
(lvgl_sys::LV_HOR_RES_MAX * 10) as u32,
|
||||
);
|
||||
}
|
||||
let mut disp_drv = unsafe {
|
||||
|
||||
// Descriptor of a display driver
|
||||
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit().assume_init();
|
||||
|
||||
// Basic initialization
|
||||
lvgl_sys::lv_disp_drv_init(&mut disp_drv);
|
||||
|
||||
// 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
|
||||
disp_drv.user_data = device as *mut _ as *mut cty::c_void;
|
||||
|
||||
disp_drv
|
||||
};
|
||||
// Assign the buffer to the display
|
||||
disp_drv.buffer = display_buffer.as_mut_ptr();
|
||||
// Finally register the driver
|
||||
unsafe {
|
||||
lvgl_sys::lv_disp_drv_register(&mut disp_drv);
|
||||
}
|
||||
Self {
|
||||
raw: disp_drv,
|
||||
display_buffer,
|
||||
refresh_buffer,
|
||||
phantom: &PhantomData,
|
||||
phantom2: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_active_screen(&mut self) -> ObjectX<'static> {
|
||||
get_active_screen()
|
||||
Self { raw: disp_drv }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ColorRgb(lv_color_t);
|
||||
// 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!()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<ColorRgb> for Rgb888 {
|
||||
fn from(color: ColorRgb) -> Self {
|
||||
// Convert Lvgl to embedded-graphics color
|
||||
let raw_color = color.0;
|
||||
unsafe {
|
||||
Rgb888::new(
|
||||
lvgl_sys::_LV_COLOR_GET_R(raw_color) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_G(raw_color) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_B(raw_color) as u8,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorRgb> for Rgb565 {
|
||||
fn from(color: ColorRgb) -> Self {
|
||||
// Convert Lvgl to embedded-graphics color
|
||||
let raw_color = color.0;
|
||||
unsafe {
|
||||
Rgb565::new(
|
||||
lvgl_sys::_LV_COLOR_GET_R(raw_color) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_G(raw_color) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_B(raw_color) as u8,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
|
@ -107,7 +86,7 @@ unsafe extern "C" fn display_callback_wrapper<T, C>(
|
|||
color_p: *mut lvgl_sys::lv_color_t,
|
||||
) where
|
||||
T: DrawTarget<C>,
|
||||
C: PixelColor + From<ColorRgb>
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
// We need to make sure panics can't escape across the FFI boundary.
|
||||
//let _ = std::panic::catch_unwind(|| {
|
||||
|
@ -121,10 +100,13 @@ unsafe extern "C" fn display_callback_wrapper<T, C>(
|
|||
//let image_buffer =
|
||||
for y in (*area).y1..=(*area).y2 {
|
||||
for x in (*area).x1..=(*area).x2 {
|
||||
let raw_color = ColorRgb(*color_p.add(i));
|
||||
let raw_color = Color::from_raw(*color_p.add(i));
|
||||
i = i + 1;
|
||||
// TODO: Use device.draw_iter
|
||||
let _ = device.draw_pixel(drawable::Pixel(Point::new(x as i32, y as i32), raw_color.into()));
|
||||
let _ = device.draw_pixel(drawable::Pixel(
|
||||
Point::new(x as i32, y as i32),
|
||||
raw_color.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,9 +114,3 @@ unsafe extern "C" fn display_callback_wrapper<T, C>(
|
|||
lvgl_sys::lv_disp_flush_ready(disp_drv);
|
||||
//}); // end of panic::catch_unwind
|
||||
}
|
||||
|
||||
pub fn get_active_screen() -> ObjectX<'static> {
|
||||
let raw =
|
||||
unsafe { ptr::NonNull::new_unchecked(lvgl_sys::lv_disp_get_scr_act(ptr::null_mut())) };
|
||||
ObjectX::new(raw)
|
||||
}
|
||||
|
|
70
lvgl/src/global.rs
Normal file
70
lvgl/src/global.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use crate::{DisplayDriver, ObjectX};
|
||||
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);
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub enum LvError {
|
||||
Uninitialized,
|
||||
AlreadyInUse,
|
||||
}
|
||||
|
||||
type LvResult<T> = Result<T, LvError>;
|
||||
|
||||
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() -> Result<Self, LvError> {
|
||||
if LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) == false {
|
||||
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) -> ObjectX {
|
||||
unsafe {
|
||||
let screen = lvgl_sys::lv_disp_get_scr_act(ptr::null_mut());
|
||||
ObjectX::from_raw(NonNull::new_unchecked(screen))
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +1,12 @@
|
|||
#![no_std]
|
||||
|
||||
pub mod display;
|
||||
mod objx;
|
||||
extern crate alloc;
|
||||
|
||||
mod api;
|
||||
mod global;
|
||||
mod display;
|
||||
mod support;
|
||||
mod widgets;
|
||||
|
||||
pub use global::{UI, LvError};
|
||||
pub use display::DisplayDriver;
|
||||
pub use objx::*;
|
||||
|
||||
|
||||
pub use support::*;
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
use alloc::boxed::Box;
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use cty;
|
||||
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
||||
use lvgl_sys;
|
||||
|
||||
pub trait NativeObject {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t>;
|
||||
}
|
||||
|
||||
pub struct ObjectX<'a> {
|
||||
pub struct ObjectX {
|
||||
raw: ptr::NonNull<lvgl_sys::lv_obj_t>,
|
||||
style: Option<&'a mut Style>,
|
||||
}
|
||||
|
||||
impl<'a> ObjectX<'a> {
|
||||
pub(crate) fn new(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
|
||||
Self { raw, style: None }
|
||||
impl ObjectX {
|
||||
pub(crate) fn from_raw(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NativeObject for ObjectX<'a> {
|
||||
impl NativeObject for ObjectX {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
|
||||
unsafe { ptr::NonNull::new_unchecked(self.raw.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Object<'a>: NativeObject {
|
||||
pub trait Object: NativeObject {
|
||||
fn set_pos(&mut self, x: i16, y: i16) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_pos(
|
||||
|
@ -95,36 +96,36 @@ pub trait Object<'a>: NativeObject {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_style(&mut self, style: &'a mut Style);
|
||||
fn set_style(&mut self, style: Style);
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> for ObjectX<'a> {
|
||||
fn set_style(&mut self, style: &'a mut Style) {
|
||||
impl Object for ObjectX {
|
||||
fn set_style(&mut self, style: Style) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), style.raw());
|
||||
let boxed = Box::new(style.raw);
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), Box::into_raw(boxed));
|
||||
};
|
||||
self.style = Some(style);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_object {
|
||||
($item:ident) => {
|
||||
pub struct $item<'a> {
|
||||
core: ObjectX<'a>,
|
||||
pub struct $item {
|
||||
core: ObjectX,
|
||||
}
|
||||
|
||||
impl<'a> NativeObject for $item<'a> {
|
||||
impl NativeObject for $item {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
|
||||
self.core.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> for $item<'a> {
|
||||
fn set_style(&mut self, style: &'a mut Style) {
|
||||
impl Object for $item {
|
||||
fn set_style(&mut self, style: Style) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), style.raw());
|
||||
let boxed = Box::new(style.raw);
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), Box::into_raw(boxed));
|
||||
};
|
||||
self.core.style = Some(style);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -132,7 +133,7 @@ macro_rules! define_object {
|
|||
|
||||
define_object!(Button);
|
||||
|
||||
impl<'a> Button<'a> {
|
||||
impl Button {
|
||||
pub fn new<C>(parent: &mut C) -> Self
|
||||
where
|
||||
C: NativeObject,
|
||||
|
@ -141,7 +142,7 @@ impl<'a> Button<'a> {
|
|||
let ptr = lvgl_sys::lv_btn_create(parent.raw().as_mut(), ptr::null_mut());
|
||||
ptr::NonNull::new_unchecked(ptr)
|
||||
};
|
||||
let core = ObjectX::new(raw);
|
||||
let core = ObjectX::from_raw(raw);
|
||||
Self { core }
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +156,7 @@ pub enum LabelAlign {
|
|||
|
||||
define_object!(Label);
|
||||
|
||||
impl<'a> Label<'a> {
|
||||
impl Label {
|
||||
pub fn new<C>(parent: &mut C) -> Self
|
||||
where
|
||||
C: NativeObject,
|
||||
|
@ -164,7 +165,7 @@ impl<'a> Label<'a> {
|
|||
let ptr = lvgl_sys::lv_label_create(parent.raw().as_mut(), ptr::null_mut());
|
||||
ptr::NonNull::new_unchecked(ptr)
|
||||
};
|
||||
let core = ObjectX::new(raw);
|
||||
let core = ObjectX::from_raw(raw);
|
||||
Self { core }
|
||||
}
|
||||
|
||||
|
@ -264,6 +265,34 @@ impl Color {
|
|||
let raw = unsafe { lvgl_sys::_LV_COLOR_MAKE(r, g, b) };
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
pub fn from_raw(raw: lvgl_sys::lv_color_t) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Rgb888 {
|
||||
fn from(color: Color) -> Self {
|
||||
unsafe {
|
||||
Rgb888::new(
|
||||
lvgl_sys::_LV_COLOR_GET_R(color.raw) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_G(color.raw) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_B(color.raw) as u8,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Rgb565 {
|
||||
fn from(color: Color) -> Self {
|
||||
unsafe {
|
||||
Rgb565::new(
|
||||
lvgl_sys::_LV_COLOR_GET_R(color.raw) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_G(color.raw) as u8,
|
||||
lvgl_sys::_LV_COLOR_GET_B(color.raw) as u8,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Align {
|
0
lvgl/src/widgets.rs
Normal file
0
lvgl/src/widgets.rs
Normal file
Loading…
Reference in a new issue