From 65286d950a29445daa0cb7968efc7278eea0bda6 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Wed, 2 Jun 2021 15:31:46 +0200 Subject: [PATCH] Update examples --- examples/arc.rs | 52 +++++++++++++++++++++------------- examples/demo.rs | 37 ++++++++++++------------ lvgl-codegen/src/analysis.rs | 25 +++++++++++++++++ lvgl-codegen/src/lib.rs | 54 ++++++++++++++++++++++++++++-------- lvgl/src/display.rs | 9 +++--- lvgl/src/functions.rs | 11 +++++--- lvgl/src/lib.rs | 2 +- lvgl/src/mem.rs | 4 +-- lvgl/src/support.rs | 12 ++++++++ lvgl/src/widgets/label.rs | 13 +++++++++ 10 files changed, 156 insertions(+), 63 deletions(-) create mode 100644 lvgl-codegen/src/analysis.rs diff --git a/examples/arc.rs b/examples/arc.rs index eb2a1f5..34197a4 100644 --- a/examples/arc.rs +++ b/examples/arc.rs @@ -4,12 +4,16 @@ use embedded_graphics::prelude::*; use embedded_graphics_simulator::{ OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window, }; +use lvgl::display::Display; use lvgl::style::Style; use lvgl::widgets::{Arc, Label, LabelAlign}; -use lvgl::{self, Align, Color, Part, State, UI}; +use lvgl::{self, Align, Color, Part, State}; use lvgl::{LvError, Widget}; use lvgl_sys; -use std::time::Instant; +use parking_lot::Mutex; +use std::sync::Arc as SyncArc; +use std::thread; +use std::time::{Duration, Instant}; fn mem_info() -> lvgl_sys::lv_mem_monitor_t { let mut info = lvgl_sys::lv_mem_monitor_t { @@ -36,7 +40,9 @@ fn main() -> Result<(), LvError> { } fn run_arc_demo() -> Result<(), LvError> { - let display: SimulatorDisplay = SimulatorDisplay::new(Size::new( + lvgl::init(); + + let embedded_graphics_display: SimulatorDisplay = SimulatorDisplay::new(Size::new( lvgl_sys::LV_HOR_RES_MAX, lvgl_sys::LV_VER_RES_MAX, )); @@ -44,52 +50,60 @@ fn run_arc_demo() -> Result<(), LvError> { let output_settings = OutputSettingsBuilder::new().scale(2).build(); let mut window = Window::new("Arc Example", &output_settings); - let mut ui = UI::init()?; + let shared_native_display = SyncArc::new(Mutex::new(embedded_graphics_display)); + let display = Display::register_shared(&shared_native_display)?; - // Implement and register your display: - ui.disp_drv_register(display)?; - - // Create screen and widgets - let mut screen = ui.scr_act()?; + let mut screen = display.get_str_act()?; let mut screen_style = Style::default(); screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((255, 255, 255))); screen_style.set_radius(State::DEFAULT, 0); - screen.add_style(Part::Main, screen_style)?; + screen.add_style(Part::Main, &mut screen_style)?; // Create the arc object - let mut arc = Arc::new(&mut screen)?; + let mut arc = Arc::new()?; arc.set_size(150, 150)?; arc.set_align(&mut screen, Align::Center, 0, 10)?; arc.set_start_angle(135)?; arc.set_end_angle(135)?; - let mut loading_lbl = Label::new(&mut screen)?; + let mut loading_lbl = Label::new()?; loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?; loading_lbl.set_align(&mut arc, Align::OutTopMid, 0, -10)?; loading_lbl.set_label_align(LabelAlign::Center)?; let mut loading_style = Style::default(); loading_style.set_text_color(State::DEFAULT, Color::from_rgb((0, 0, 0))); - loading_lbl.add_style(Part::Main, loading_style)?; + loading_lbl.add_style(Part::Main, &mut loading_style)?; let mut angle = 0; let mut forward = true; let mut i = 0; - let mut loop_started = Instant::now(); + // LVGL timer thread + thread::spawn(|| { + let interval = Duration::from_millis(5); + loop { + thread::sleep(interval); + lvgl::tick_inc(interval); + } + }); + 'running: loop { if i > 270 { forward = if forward { false } else { true }; i = 1; - println!("meminfo running: {:?}", mem_info()); + println!("mem info running: {:?}", mem_info()); } angle = if forward { angle + 1 } else { angle - 1 }; arc.set_end_angle(angle + 135)?; i += 1; - ui.task_handler(); - window.update(ui.get_display_ref().unwrap()); + lvgl::task_handler(); + { + let eg_display = shared_native_display.lock(); + window.update(&eg_display); + } for event in window.events() { match event { @@ -97,9 +111,7 @@ fn run_arc_demo() -> Result<(), LvError> { _ => {} } } - - ui.tick_inc(loop_started.elapsed()); - loop_started = Instant::now(); + thread::sleep(Duration::from_millis(15)); } Ok(()) diff --git a/examples/demo.rs b/examples/demo.rs index f6da8e6..206dba6 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -12,25 +12,24 @@ use lvgl::{Align, Color, LvError, Part, State, Widget, UI}; use lvgl_sys; use parking_lot::Mutex; use std::sync::Arc as SyncArc; +use std::thread; use std::thread::sleep; use std::time::{Duration, Instant}; fn main() -> Result<(), LvError> { + lvgl::init(); + let embedded_graphics_display: SimulatorDisplay = SimulatorDisplay::new(Size::new( lvgl_sys::LV_HOR_RES_MAX, lvgl_sys::LV_VER_RES_MAX, )); - let output_settings = OutputSettingsBuilder::new().scale(2).build(); + let output_settings = OutputSettingsBuilder::new().scale(1).build(); let mut window = Window::new("PineTime", &output_settings); - lvgl::init(); - // let mut ui = UI::init()?; - // Implement and register your display: let shared_native_display = SyncArc::new(Mutex::new(embedded_graphics_display)); let _display = Display::register_shared(&shared_native_display)?; - // ui.disp_drv_register(embedded_graphics_display).unwrap(); // Create screen and widgets let mut screen = DefaultDisplay::get_scr_act()?; @@ -40,52 +39,52 @@ fn main() -> Result<(), LvError> { screen_style.set_radius(State::DEFAULT, 0); screen.add_style(Part::Main, &mut screen_style)?; - let mut time = Label::new()?; + let mut time = Label::from("20:46"); let mut style_time = Style::default(); // style_time.set_text_font(font_noto_sans_numeric_28); style_time.set_text_color(State::DEFAULT, Color::from_rgb((255, 255, 255))); time.add_style(Part::Main, &mut style_time)?; time.set_align(&mut 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 mut bt = Label::new()?; + let mut bt = Label::from("#5794f2 \u{F293}#"); bt.set_width(50)?; 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(&mut screen, Align::InTopLeft, 0, 0)?; - let mut power = Label::new()?; + let mut power: Label = "#fade2a 20%#".into(); power.set_recolor(true)?; power.set_width(80)?; power.set_height(20)?; - power.set_text(CString::new("#fade2a 20%#").unwrap().as_c_str())?; power.set_label_align(LabelAlign::Right)?; power.set_align(&mut screen, Align::InTopRight, 0, 0)?; + // LVGL timer thread + thread::spawn(|| { + let interval = Duration::from_millis(5); + loop { + thread::sleep(interval); + lvgl::tick_inc(interval); + } + }); + let mut i = 0; - let mut loop_started = Instant::now(); 'running: loop { if i > 59 { i = 0; } - println!("i = {}", i); let val = CString::new(format!("21:{:02}", i)).unwrap(); time.set_text(&val)?; i = 1 + i; - let wait_for = lvgl::task_handler(); - println!("wait for = {} ms", wait_for.unwrap().as_secs()); + lvgl::task_handler(); { let native_display = shared_native_display.lock(); window.update(&native_display); } - // ui.task_handler(); - // window.update(ui.get_display_ref().unwrap()); - lvgl::tick_inc(loop_started.elapsed()); for event in window.events() { match event { @@ -94,8 +93,6 @@ fn main() -> Result<(), LvError> { } } sleep(Duration::from_secs(1)); - - loop_started = Instant::now(); } Ok(()) diff --git a/lvgl-codegen/src/analysis.rs b/lvgl-codegen/src/analysis.rs new file mode 100644 index 0000000..7af6991 --- /dev/null +++ b/lvgl-codegen/src/analysis.rs @@ -0,0 +1,25 @@ +/// A parameter of C functions. +/// +/// This struct represents all relevant information we can extract from the C function declaration +/// of a LVGL public interface. We can use this information to do inference for how the parameter +/// should be represented in a safe Rust API. +pub struct CParameter { + /// The name of the parameter in the C code. + name: String, + + /// This is the raw representation of the Rust equivalent of the C type. + c_type: String, + + /// Takes a pointer to a type that is referenced by the LVGL code permanently. + owned: bool, + + /// The pointer is not marked as `*const` so the referenced object can be mutated. + mutable: bool, + + /// We need to check if the value is optional in the C code. We need to check + /// the function comments for this information. + /// - "if NULL then" + /// - "if not NULL then" + /// - "NULL to" + nullable: bool, +} diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 9fb182f..e8ca44c 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -1,3 +1,5 @@ +mod analysis; + use inflector::cases::pascalcase::to_pascal_case; use lazy_static::lazy_static; use proc_macro2::{Ident, TokenStream}; @@ -44,6 +46,12 @@ pub struct LvWidget { methods: Vec, } +impl LvWidget { + fn pascal_name(&self) -> String { + to_pascal_case(&self.name) + } +} + impl Rusty for LvWidget { type Parent = (); @@ -53,7 +61,7 @@ impl Rusty for LvWidget { return Err(WrapperError::Skip); } - let widget_name = format_ident!("{}", to_pascal_case(self.name.as_str())); + let widget_name = format_ident!("{}", self.pascal_name()); let methods: Vec = self.methods.iter().flat_map(|m| m.code(self)).collect(); Ok(quote! { define_object!(#widget_name); @@ -90,6 +98,7 @@ impl Rusty for LvFunc { type Parent = LvWidget; fn code(&self, parent: &Self::Parent) -> WrapperResult { + let widget_name = format_ident!("{}", parent.pascal_name()); let templ = format!("{}{}_", LIB_PREFIX, parent.name.as_str()); let new_name = self.name.replace(templ.as_str(), ""); let func_name = format_ident!("{}", new_name); @@ -99,12 +108,12 @@ impl Rusty for LvFunc { if new_name.as_str().eq("create") { return Ok(quote! { - pub fn new(parent: &mut C) -> crate::LvResult - where - C: crate::NativeObject, - { + pub fn create(parent: &mut impl crate::NativeObject, copy: Option<&#widget_name>) -> crate::LvResult { 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(), + copy.map(|c| c.raw().unwrap().as_mut() as *mut lvgl_sys::lv_obj_t).unwrap_or(core::ptr::null_mut() as *mut lvgl_sys::lv_obj_t), + ); if let Some(raw) = core::ptr::NonNull::new(ptr) { let core = ::from_raw(raw); Ok(Self { core }) @@ -114,6 +123,15 @@ impl Rusty for LvFunc { } } + pub fn create_at(parent: &mut impl crate::NativeObject) -> crate::LvResult { + Ok(Self::create(parent, None)?) + } + + pub fn new() -> crate::LvResult { + let mut parent = crate::display::DefaultDisplay::get_scr_act()?; + Ok(Self::create_at(&mut parent)?) + } + }); } @@ -338,6 +356,9 @@ impl Rusty for LvType { Some(name) => { let val = if self.is_str() { quote!(&cstr_core::CStr) + } else if (self.literal_name.contains("lv_")) { + let ident = format_ident!("{}", name); + quote!(&#ident) } else { let ident = format_ident!("{}", name); quote!(#ident) @@ -631,13 +652,12 @@ mod test { define_object!(Arc); impl Arc { - pub fn new(parent: &mut C) -> crate::LvResult - where - C: crate::NativeObject, - { - + pub fn create(parent: &mut impl crate::NativeObject, copy: Option<&Arc>) -> crate::LvResult { 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(), + copy.map(|c| c.raw().unwrap().as_mut() as *mut lvgl_sys::lv_obj_t).unwrap_or(core::ptr::null_mut() as *mut lvgl_sys::lv_obj_t), + ); if let Some(raw) = core::ptr::NonNull::new(ptr) { let core = ::from_raw(raw); Ok(Self { core }) @@ -646,6 +666,16 @@ mod test { } } } + + pub fn create_at(parent: &mut impl crate::NativeObject) -> crate::LvResult { + Ok(Self::create(parent, None)?) + } + + pub fn new() -> crate::LvResult { + Ok(Self::create_at( + &mut crate::display::DefaultDisplay::get_scr_act(), + )?) + } } }; diff --git a/lvgl/src/display.rs b/lvgl/src/display.rs index 846ae12..20bc7b0 100644 --- a/lvgl/src/display.rs +++ b/lvgl/src/display.rs @@ -10,7 +10,7 @@ use embedded_graphics::drawable; use embedded_graphics::prelude::*; #[cfg(feature = "alloc")] -use parking_lot::Mutex; +use parking_lot::Mutex; // TODO: Can this really be used in no_std envs with alloc? #[cfg(feature = "alloc")] use alloc::sync::Arc; @@ -90,14 +90,15 @@ impl DisplayBuffer { pub fn new() -> Self { let disp_buf = unsafe { let mut disp_buf = MaybeUninit::uninit(); + // TODO: Need to find a way to not add this to LVGL memory. let mut refresh_buffer1 = Box::new([lvgl_sys::lv_color_t::default(); BUF_SIZE]); - let mut refresh_buffer2 = Box::new([lvgl_sys::lv_color_t::default(); BUF_SIZE]); + //let mut refresh_buffer2 = Box::new([lvgl_sys::lv_color_t::default(); BUF_SIZE]); // let refresh_buffer2 = [lvgl_sys::lv_color_t::default(); BUF_SIZE]; lvgl_sys::lv_disp_buf_init( disp_buf.as_mut_ptr(), Box::into_raw(refresh_buffer1) as *mut cty::c_void, - Box::into_raw(refresh_buffer2) as *mut cty::c_void, - //ptr::null_mut(), + // Box::into_raw(refresh_buffer2) as *mut cty::c_void, + ptr::null_mut(), lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32, ); disp_buf.assume_init() diff --git a/lvgl/src/functions.rs b/lvgl/src/functions.rs index 824c8ac..386b6c7 100644 --- a/lvgl/src/functions.rs +++ b/lvgl/src/functions.rs @@ -42,6 +42,10 @@ pub(crate) fn get_str_act(disp: Option<&Display>) -> Result { )) } +/// You have to call this function periodically. +/// Expects a `tick_period` duration as argument which is the call period of this +/// function in milliseconds. +#[inline] pub fn tick_inc(tick_period: Duration) { unsafe { lvgl_sys::lv_tick_inc(tick_period.as_millis() as u32); @@ -49,8 +53,7 @@ pub fn tick_inc(tick_period: Duration) { } /// Call it periodically to handle tasks. -/// Returns the duration after which it must be called again. -pub fn task_handler() -> Option { - let next_call_at = unsafe { lvgl_sys::lv_task_handler() }; - Some(Duration::from_secs(next_call_at as u64)) +#[inline] +pub fn task_handler() { + unsafe { lvgl_sys::lv_task_handler() }; } diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index 7ab127f..5f00c41 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -23,7 +23,7 @@ extern crate lazy_static; #[macro_use] mod lv_core; -#[cfg(feature = "lvgl_alloc")] +#[cfg(feature = "alloc")] extern crate alloc; // We can ONLY use `alloc::boxed::Box` if `lvgl_alloc` is enabled. diff --git a/lvgl/src/mem.rs b/lvgl/src/mem.rs index 9b020b3..b90dbd3 100644 --- a/lvgl/src/mem.rs +++ b/lvgl/src/mem.rs @@ -36,8 +36,8 @@ impl Box { } pub fn into_raw(self) -> *mut T { - let b = mem::ManuallyDrop::new(self.0); - b.as_ptr() + let b = mem::ManuallyDrop::new(self); + b.0.as_ptr() } } diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index c3861ae..552386a 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -1,3 +1,4 @@ +use crate::display::DisplayError; use crate::Widget; use core::convert::{TryFrom, TryInto}; use core::ptr::NonNull; @@ -13,6 +14,17 @@ pub enum LvError { AlreadyInUse, } +impl From for LvError { + fn from(err: DisplayError) -> Self { + use LvError::*; + match err { + DisplayError::NotAvailable => Uninitialized, + DisplayError::FailedToRegister => InvalidReference, + DisplayError::NotRegistered => Uninitialized, + } + } +} + #[derive(Clone)] pub struct Color { pub(crate) raw: lvgl_sys::lv_color_t, diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index a18ecc3..4e99bd3 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -1,6 +1,9 @@ use crate::widgets::Label; use crate::{LvResult, NativeObject}; +#[cfg(feature = "alloc")] +use cstr_core::CString; + impl Label { pub fn set_label_align(&mut self, align: LabelAlign) -> LvResult<()> { unsafe { @@ -10,6 +13,16 @@ impl Label { } } +#[cfg(feature = "alloc")] +impl> From for Label { + fn from(text: S) -> Self { + let text_cstr = CString::new(text.as_ref()).unwrap(); + let mut label = Label::new().unwrap(); + label.set_text(text_cstr.as_c_str()).unwrap(); + label + } +} + #[derive(Debug, Copy, Clone, PartialEq)] #[repr(u8)] pub enum LabelAlign {