From a7bef1cd54a9294767027e9e96d625c73f55dcfa Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Thu, 28 May 2020 22:12:29 +0200 Subject: [PATCH 1/6] New API test using std/alloc objects --- lvgl/src/api.rs | 207 ++++++++++++++++++++++++++++++++++++++++++++++++ lvgl/src/lib.rs | 6 +- 2 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 lvgl/src/api.rs diff --git a/lvgl/src/api.rs b/lvgl/src/api.rs new file mode 100644 index 0000000..dce81ef --- /dev/null +++ b/lvgl/src/api.rs @@ -0,0 +1,207 @@ +use core::marker::PhantomData; +use core::time::Duration; +use core::sync::atomic::{AtomicBool, Ordering}; + +// 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 ()> +} + +impl UI { + pub fn init() -> Result { + if LVGL_IN_USE.compare_and_swap(false, true, Ordering::SeqCst) == false { + // lvgl_sys::lv_init(); + Ok(Self { + _not_send: PhantomData, + }) + } else { + Err(LvError::AlreadyInUse) + } + } + + pub fn disp_drv_register(&mut self, _display: &mut DisplayDriver) { + // register it + // lvgl_sys::lv_disp_drv_register(&mut disp_drv); + } + + 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(&mut self) -> &mut Screen { + &mut self.current_screen + } +} + +pub trait LvObject { + fn label_create(&self) -> Label where Self: Sized { + Label{ parent: self } + } + + fn btn_create(&self) -> Button 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, +} + +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; + use std::sync::{Mutex, Arc}; + + #[test] + fn basic_usage() { + let mut ui = UI::init().unwrap(); + + let refresh_lines = 10; + let mut display = DisplayDriver::new(refresh_lines); + ui.disp_drv_register(&mut display); + let display = Arc::new(Mutex::new(display)); + + { + let mut disp = display.clone(); + let mut d = disp.lock().unwrap(); + let screen = d.scr_act(); + + let mut button = screen.btn_create(); + + let mut inner_disp = display.clone(); + button.on_event(|| { + // something + let mut disp = inner_disp.lock().unwrap(); + let screen = disp.scr_act(); + let mut label = screen.label_create(); + label.set_text("Clicked"); + }); + + let mut label = button.label_create(); + label.set_text("Click me!"); + }; + + { + let mut disp = display.clone(); + let mut d = disp.lock().unwrap(); + let screen = d.scr_act(); + 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> 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(); + } +} diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index aa5e592..e5956ca 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -1,7 +1,11 @@ -#![no_std] +// #![no_std] pub mod display; mod objx; +mod api; + pub use display::DisplayDriver; pub use objx::*; + + -- 2.34.1 From 1f083782451e2bd975364f8208bbd2a704e1cdf3 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Thu, 28 May 2020 22:37:13 +0200 Subject: [PATCH 2/6] Use RefCell to create multiple mut refs --- lvgl/src/api.rs | 13 +++++-------- lvgl/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lvgl/src/api.rs b/lvgl/src/api.rs index dce81ef..aa7f9ff 100644 --- a/lvgl/src/api.rs +++ b/lvgl/src/api.rs @@ -114,7 +114,7 @@ impl<'a, F> LvObject for Button<'a, F> where F: FnMut() {} mod test { use crate::api::{UI, DisplayDriver, LvObject}; use core::time::Duration; - use std::sync::{Mutex, Arc}; + use core::cell::RefCell; #[test] fn basic_usage() { @@ -123,19 +123,17 @@ mod test { let refresh_lines = 10; let mut display = DisplayDriver::new(refresh_lines); ui.disp_drv_register(&mut display); - let display = Arc::new(Mutex::new(display)); + let display = RefCell::new(display); { - let mut disp = display.clone(); - let mut d = disp.lock().unwrap(); + let mut d = display.borrow_mut(); let screen = d.scr_act(); let mut button = screen.btn_create(); - let mut inner_disp = display.clone(); button.on_event(|| { // something - let mut disp = inner_disp.lock().unwrap(); + let mut disp = display.borrow_mut(); let screen = disp.scr_act(); let mut label = screen.label_create(); label.set_text("Clicked"); @@ -146,8 +144,7 @@ mod test { }; { - let mut disp = display.clone(); - let mut d = disp.lock().unwrap(); + let mut d = display.borrow_mut(); let screen = d.scr_act(); let mut button2 = screen.btn_create(); button2.on_event(|| { diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index e5956ca..72735f0 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -1,4 +1,4 @@ -// #![no_std] +#![no_std] pub mod display; mod objx; -- 2.34.1 From 11bbd979fd5f68a51e123b7c76ec9349830e970b Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Fri, 29 May 2020 00:01:37 +0200 Subject: [PATCH 3/6] UI saves display obj, makes possible for global call to active display --- lvgl/src/api.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/lvgl/src/api.rs b/lvgl/src/api.rs index aa7f9ff..5c17581 100644 --- a/lvgl/src/api.rs +++ b/lvgl/src/api.rs @@ -1,6 +1,7 @@ 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); @@ -12,7 +13,8 @@ pub enum LvError { pub struct UI { // LittlevGL is not thread-safe by default. - _not_send: PhantomData<*const ()> + _not_send: PhantomData<*const ()>, + display: Option, } impl UI { @@ -21,15 +23,26 @@ impl UI { // lvgl_sys::lv_init(); Ok(Self { _not_send: PhantomData, + display: None, }) } else { Err(LvError::AlreadyInUse) } } - pub fn disp_drv_register(&mut self, _display: &mut DisplayDriver) { + 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) { @@ -56,8 +69,8 @@ impl DisplayDriver { self.current_screen = screen; } - pub fn scr_act(&mut self) -> &mut Screen { - &mut self.current_screen + pub fn scr_act(&self) -> &Screen { + &self.current_screen } } @@ -114,27 +127,22 @@ impl<'a, F> LvObject for Button<'a, F> where F: FnMut() {} mod test { use crate::api::{UI, DisplayDriver, LvObject}; use core::time::Duration; - use core::cell::RefCell; #[test] fn basic_usage() { let mut ui = UI::init().unwrap(); let refresh_lines = 10; - let mut display = DisplayDriver::new(refresh_lines); - ui.disp_drv_register(&mut display); - let display = RefCell::new(display); + let display = DisplayDriver::new(refresh_lines); + ui.disp_drv_register(display); { - let mut d = display.borrow_mut(); - let screen = d.scr_act(); - + let screen = ui.scr_act().unwrap(); let mut button = screen.btn_create(); button.on_event(|| { // something - let mut disp = display.borrow_mut(); - let screen = disp.scr_act(); + let screen = ui.scr_act().unwrap(); let mut label = screen.label_create(); label.set_text("Clicked"); }); @@ -144,8 +152,7 @@ mod test { }; { - let mut d = display.borrow_mut(); - let screen = d.scr_act(); + let screen = ui.scr_act().unwrap(); let mut button2 = screen.btn_create(); button2.on_event(|| { // else -- 2.34.1 From 8856b83461842d2417282c411deb16e71b947f13 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Fri, 29 May 2020 23:42:39 +0200 Subject: [PATCH 4/6] Reflect restrictions of LvGL --- examples/demo/src/main.rs | 43 ++++--- lvgl/src/api.rs | 211 ------------------------------- lvgl/src/display.rs | 150 +++++++++------------- lvgl/src/global.rs | 70 ++++++++++ lvgl/src/lib.rs | 13 +- lvgl/src/{objx.rs => support.rs} | 75 +++++++---- lvgl/src/widgets.rs | 0 7 files changed, 214 insertions(+), 348 deletions(-) delete mode 100644 lvgl/src/api.rs create mode 100644 lvgl/src/global.rs rename lvgl/src/{objx.rs => support.rs} (81%) create mode 100644 lvgl/src/widgets.rs diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index 3cf9fb4..c0a55f5 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -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 = 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); diff --git a/lvgl/src/api.rs b/lvgl/src/api.rs deleted file mode 100644 index 5c17581..0000000 --- a/lvgl/src/api.rs +++ /dev/null @@ -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, -} - -impl UI { - pub fn init() -> Result { - 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(&self) -> Button 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, -} - -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> 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(); - } -} diff --git a/lvgl/src/display.rs b/lvgl/src/display.rs index 5f641f6..edc63ee 100644 --- a/lvgl/src/display.rs +++ b/lvgl/src/display.rs @@ -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: PixelColor + From -{ - raw: lvgl_sys::lv_disp_drv_t, - display_buffer: MaybeUninit, - refresh_buffer: [MaybeUninit; lvgl_sys::LV_HOR_RES_MAX as usize * 10], - phantom: &'a PhantomData, - phantom2: PhantomData, +pub struct DisplayDriver { + pub(crate) raw: lvgl_sys::lv_disp_drv_t, } -impl<'a, T, C> DisplayDriver<'a, T, C> -where - T: DrawTarget, - C: PixelColor + From -{ - pub fn new(device: &'a mut T) -> Self { - // Create a display buffer for LittlevGL - let mut display_buffer = MaybeUninit::::uninit(); - // Declare a buffer for 10 lines - let mut refresh_buffer: [MaybeUninit; - 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> and throw it in a box and add to the user_data of the callback handler function + pub fn new(device: &mut T) -> Self + where + T: DrawTarget, + C: PixelColor + From, + { + let disp_drv = unsafe { + // Create a display buffer for LittlevGL + let mut display_buffer = + Box::new(MaybeUninit::::uninit().assume_init()); + + // Declare a buffer for the refresh rate + let refresh_buffer = Box::new( + MaybeUninit::< + [MaybeUninit; 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::::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::); + + // 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 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 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>; +// +// impl Drop for DrawHandler { +// fn drop(&mut self) { +// unimplemented!() +// } +// } unsafe extern "C" fn display_callback_wrapper( disp_drv: *mut lvgl_sys::lv_disp_drv_t, @@ -107,7 +86,7 @@ unsafe extern "C" fn display_callback_wrapper( color_p: *mut lvgl_sys::lv_color_t, ) where T: DrawTarget, - C: PixelColor + From + C: PixelColor + From, { // 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( //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( 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) -} diff --git a/lvgl/src/global.rs b/lvgl/src/global.rs new file mode 100644 index 0000000..d47b6f1 --- /dev/null +++ b/lvgl/src/global.rs @@ -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 = Result; + +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 { + 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(); + } + } +} diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index 72735f0..56268a8 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -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::*; diff --git a/lvgl/src/objx.rs b/lvgl/src/support.rs similarity index 81% rename from lvgl/src/objx.rs rename to lvgl/src/support.rs index 8aa5f13..de15044 100644 --- a/lvgl/src/objx.rs +++ b/lvgl/src/support.rs @@ -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; } -pub struct ObjectX<'a> { +pub struct ObjectX { raw: ptr::NonNull, - style: Option<&'a mut Style>, } -impl<'a> ObjectX<'a> { - pub(crate) fn new(raw: ptr::NonNull) -> Self { - Self { raw, style: None } +impl ObjectX { + pub(crate) fn from_raw(raw: ptr::NonNull) -> Self { + Self { raw } } } -impl<'a> NativeObject for ObjectX<'a> { +impl NativeObject for ObjectX { fn raw(&self) -> ptr::NonNull { 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 { 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(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(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 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 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 { diff --git a/lvgl/src/widgets.rs b/lvgl/src/widgets.rs new file mode 100644 index 0000000..e69de29 -- 2.34.1 From 2627911ff63314d0445f37afb524468bbaa09944 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 30 May 2020 00:08:46 +0200 Subject: [PATCH 5/6] No need for manual nul byte --- examples/demo/src/main.rs | 17 ++++++++++++----- lvgl/Cargo.toml | 1 + lvgl/src/global.rs | 2 -- lvgl/src/support.rs | 12 +++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index c0a55f5..26836ac 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -17,7 +17,7 @@ fn main() -> Result<(), String> { )); let output_settings = OutputSettingsBuilder::new().scale(4).build(); - let mut window = Window::new("Hello World", &output_settings); + let mut window = Window::new("PineTime", &output_settings); let mut ui = UI::init().unwrap(); @@ -42,7 +42,7 @@ fn main() -> Result<(), String> { style_time.set_text_color(lvgl::Color::from_rgb((255, 255, 255))); time.set_style(style_time); time.set_align(&mut screen, lvgl::Align::InLeftMid, 20, 0); - time.set_text("20:46\0"); + time.set_text("20:46"); time.set_width(240); time.set_height(240); @@ -54,7 +54,7 @@ fn main() -> Result<(), String> { bt.set_width(50); bt.set_height(80); bt.set_recolor(true); - bt.set_text("#5794f2 \u{F293}#\0"); + bt.set_text("#5794f2 \u{F293}#"); bt.set_label_align(lvgl::LabelAlign::Left); bt.set_align(&mut screen, lvgl::Align::InTopLeft, 0, 0); @@ -63,7 +63,7 @@ fn main() -> Result<(), String> { power.set_recolor(true); power.set_width(80); power.set_height(20); - power.set_text("#fade2a 20%#\0"); + power.set_text("#fade2a 20%#"); power.set_label_align(lvgl::LabelAlign::Right); power.set_align(&mut screen, lvgl::Align::InTopRight, 0, 0); @@ -86,7 +86,7 @@ fn main() -> Result<(), String> { if i > 59 { i = 0; } - time.set_text(format!("21:{:02}\0", i).as_str()); + time.set_text(format!("21:{:02}", i).as_str()); i = 1 + i; sleep(Duration::from_millis( @@ -112,6 +112,13 @@ fn main() -> Result<(), String> { } // Reference to native font for LittlevGL, defined in the file: "fonts_noto_sans_numeric_80.c" +// TODO: Create a macro for definiting a safe wrapper for fonts. +// Maybe sometihng like: +// +// font! { +// NotoSansNumeric80 = noto_sans_numeric_80; +// }; +// extern "C" { pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t; } diff --git a/lvgl/Cargo.toml b/lvgl/Cargo.toml index db865c1..2165e32 100644 --- a/lvgl/Cargo.toml +++ b/lvgl/Cargo.toml @@ -16,3 +16,4 @@ keywords = ["littlevgl", "lvgl", "graphical_interfaces"] lvgl-sys = {path="../lvgl-sys", version="0.1"} cty = "0.2" embedded-graphics = "0.6" +cstr_core = { version = "0.2", default-features = false, features = ["alloc"] } diff --git a/lvgl/src/global.rs b/lvgl/src/global.rs index d47b6f1..521af32 100644 --- a/lvgl/src/global.rs +++ b/lvgl/src/global.rs @@ -15,8 +15,6 @@ pub enum LvError { AlreadyInUse, } -type LvResult = Result; - pub struct UI { // LittlevGL is not thread-safe by default. _not_sync: PhantomData<*mut ()>, diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index de15044..bb545d2 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; use core::mem; use core::ptr; -use cty; +use cstr_core::CString; use embedded_graphics::pixelcolor::{Rgb565, Rgb888}; use lvgl_sys; @@ -170,11 +170,9 @@ impl Label { } pub fn set_text(&mut self, text: &str) { + let text = CString::new(text).unwrap(); unsafe { - lvgl_sys::lv_label_set_text( - self.core.raw().as_mut(), - text.as_ptr() as *const cty::c_char, - ); + lvgl_sys::lv_label_set_text(self.core.raw().as_mut(), text.as_ptr()); } } @@ -234,10 +232,6 @@ impl Style { pub fn set_text_font(&mut self, font: &lvgl_sys::lv_font_t) { self.raw.text.font = font; } - - fn raw(&mut self) -> *const lvgl_sys::lv_style_t { - &mut self.raw - } } impl Clone for Style { -- 2.34.1 From 370acdaeaf3ab9b164aa8f28a183228674fd3b01 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 30 May 2020 00:18:01 +0200 Subject: [PATCH 6/6] Fix typo --- examples/demo/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index 26836ac..592dc4e 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -112,7 +112,7 @@ fn main() -> Result<(), String> { } // Reference to native font for LittlevGL, defined in the file: "fonts_noto_sans_numeric_80.c" -// TODO: Create a macro for definiting a safe wrapper for fonts. +// TODO: Create a macro for defining a safe wrapper for fonts. // Maybe sometihng like: // // font! { -- 2.34.1