API re-design #9

Merged
rafaelcaricio merged 6 commits from api_design_studies into master 2020-05-29 22:25:28 +00:00
7 changed files with 229 additions and 148 deletions

View file

@ -1,13 +1,14 @@
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; use lvgl;
use lvgl::Object; use lvgl::{Object, UI};
use lvgl_sys; use lvgl_sys;
use std::sync::mpsc; use std::sync::{mpsc, Arc, Mutex};
use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use embedded_graphics::pixelcolor::{Rgb565};
fn main() -> Result<(), String> { fn main() -> Result<(), String> {
let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new( let mut display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
@ -16,17 +17,16 @@ fn main() -> Result<(), String> {
)); ));
let output_settings = OutputSettingsBuilder::new().scale(4).build(); 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);
unsafe { let mut ui = UI::init().unwrap();
lvgl_sys::lv_init();
}
// Implement and register your display: // 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 // 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_roboto_28 = unsafe { &lvgl_sys::lv_font_roboto_28 };
let font_noto_sans_numeric_28 = unsafe { &noto_sans_numeric_80 }; let font_noto_sans_numeric_28 = unsafe { &noto_sans_numeric_80 };
@ -34,45 +34,48 @@ fn main() -> Result<(), String> {
let mut screen_style = lvgl::Style::new(); let mut screen_style = lvgl::Style::new();
screen_style.set_body_main_color(lvgl::Color::from_rgb((0, 0, 0))); 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_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 time = lvgl::Label::new(&mut screen);
let mut style_time = lvgl::Style::new(); let mut style_time = lvgl::Style::new();
style_time.set_text_font(font_noto_sans_numeric_28); style_time.set_text_font(font_noto_sans_numeric_28);
style_time.set_text_color(lvgl::Color::from_rgb((255, 255, 255))); 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_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_width(240);
time.set_height(240); time.set_height(240);
let mut bt = lvgl::Label::new(&mut screen); let mut bt = lvgl::Label::new(&mut screen);
let mut style_bt = lvgl::Style::new(); let mut style_bt = lvgl::Style::new();
style_bt.set_text_font(font_roboto_28); style_bt.set_text_font(font_roboto_28);
let mut style_power = style_bt.clone(); let style_power = style_bt.clone();
bt.set_style(&mut style_bt); bt.set_style(style_bt);
bt.set_width(50); bt.set_width(50);
bt.set_height(80); bt.set_height(80);
bt.set_recolor(true); 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_label_align(lvgl::LabelAlign::Left);
bt.set_align(&mut screen, lvgl::Align::InTopLeft, 0, 0); bt.set_align(&mut screen, lvgl::Align::InTopLeft, 0, 0);
let mut power = lvgl::Label::new(&mut screen); 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_recolor(true);
power.set_width(80); power.set_width(80);
power.set_height(20); power.set_height(20);
power.set_text("#fade2a 20%#\0"); power.set_text("#fade2a 20%#");
power.set_label_align(lvgl::LabelAlign::Right); power.set_label_align(lvgl::LabelAlign::Right);
power.set_align(&mut screen, lvgl::Align::InTopRight, 0, 0); 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 (stop_ch, read_ch) = mpsc::channel();
let closure_ui = threaded_ui.clone();
let tick_thr = std::thread::spawn(move || loop { let tick_thr = std::thread::spawn(move || loop {
::std::thread::sleep(Duration::from_millis(5)); let period = Duration::from_millis(5);
unsafe { closure_ui.lock().unwrap().tick_inc(period);
lvgl_sys::lv_tick_inc(5);
} sleep(period);
if read_ch.try_recv().is_ok() { if read_ch.try_recv().is_ok() {
break; break;
} }
@ -83,16 +86,14 @@ fn main() -> Result<(), String> {
if i > 59 { if i > 59 {
i = 0; i = 0;
} }
time.set_text(format!("21:{:02}\0", i).as_str()); time.set_text(format!("21:{:02}", i).as_str());
i = 1 + i; i = 1 + i;
::std::thread::sleep(Duration::from_millis( sleep(Duration::from_millis(
lvgl_sys::LV_DISP_DEF_REFR_PERIOD as u64, lvgl_sys::LV_DISP_DEF_REFR_PERIOD as u64,
)); ));
unsafe { threaded_ui.lock().unwrap().task_handler();
lvgl_sys::lv_task_handler();
}
window.update(&display); window.update(&display);
@ -111,6 +112,13 @@ fn main() -> Result<(), String> {
} }
// Reference to native font for LittlevGL, defined in the file: "fonts_noto_sans_numeric_80.c" // Reference to native font for LittlevGL, defined in the file: "fonts_noto_sans_numeric_80.c"
// TODO: Create a macro for defining a safe wrapper for fonts.
// Maybe sometihng like:
//
// font! {
// NotoSansNumeric80 = noto_sans_numeric_80;
// };
//
extern "C" { extern "C" {
pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t; pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t;
} }

View file

@ -16,3 +16,4 @@ keywords = ["littlevgl", "lvgl", "graphical_interfaces"]
lvgl-sys = {path="../lvgl-sys", version="0.1"} lvgl-sys = {path="../lvgl-sys", version="0.1"}
cty = "0.2" cty = "0.2"
embedded-graphics = "0.6" embedded-graphics = "0.6"
cstr_core = { version = "0.2", default-features = false, features = ["alloc"] }

View file

@ -1,105 +1,84 @@
use crate::objx::ObjectX; use crate::Color;
use core::marker::PhantomData; use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use core::ptr;
use embedded_graphics; use embedded_graphics;
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
use embedded_graphics::prelude::*; use embedded_graphics::prelude::*;
use embedded_graphics::{drawable, DrawTarget}; use embedded_graphics::{drawable, DrawTarget};
use lvgl_sys::lv_color_t;
pub struct DisplayDriver<'a, T, C> pub struct DisplayDriver {
where pub(crate) raw: lvgl_sys::lv_disp_drv_t,
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>,
} }
impl<'a, T, C> DisplayDriver<'a, T, C> impl DisplayDriver {
where // we should accept a Rc<RefCell<T>> and throw it in a box and add to the user_data of the callback handler function
T: DrawTarget<C>, pub fn new<T, C>(device: &mut T) -> Self
C: PixelColor + From<ColorRgb> where
{ T: DrawTarget<C>,
pub fn new(device: &'a mut T) -> Self { C: PixelColor + From<Color>,
// Create a display buffer for LittlevGL {
let mut display_buffer = MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit(); let disp_drv = unsafe {
// Declare a buffer for 10 lines // Create a display buffer for LittlevGL
let mut refresh_buffer: [MaybeUninit<lvgl_sys::lv_color_t>; let mut display_buffer =
lvgl_sys::LV_HOR_RES_MAX as usize * 10] = Box::new(MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit().assume_init());
unsafe { MaybeUninit::uninit().assume_init() };
// Initialize the display buffer // Declare a buffer for the refresh rate
unsafe { 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( lvgl_sys::lv_disp_buf_init(
display_buffer.as_mut_ptr(), display_buffer.as_mut(),
refresh_buffer.as_mut_ptr() as *mut cty::c_void, Box::into_raw(refresh_buffer) as *mut cty::c_void,
core::ptr::null_mut(), core::ptr::null_mut(),
(lvgl_sys::LV_HOR_RES_MAX * 10) as u32, (lvgl_sys::LV_HOR_RES_MAX * 10) as u32,
); );
}
let mut disp_drv = unsafe {
// Descriptor of a display driver // Descriptor of a display driver
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit().assume_init(); let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit().assume_init();
// Basic initialization // Basic initialization
lvgl_sys::lv_disp_drv_init(&mut disp_drv); 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 // Set your driver function
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>); 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.user_data = device as *mut _ as *mut cty::c_void;
disp_drv disp_drv
}; };
// Assign the buffer to the display Self { raw: disp_drv }
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()
} }
} }
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 { // a reference is kept to the external drawing target (T)
fn from(color: ColorRgb) -> Self { // the reference is kept in the callback function of the drawing handler
// Convert Lvgl to embedded-graphics color // we need a reference counter for the drawing target and free the ref counter when the display is
let raw_color = color.0; // destroyed.
unsafe { type DrawHandler = Rc<RefCell<u8>>;
Rgb888::new( //
lvgl_sys::_LV_COLOR_GET_R(raw_color) as u8, // impl Drop for DrawHandler {
lvgl_sys::_LV_COLOR_GET_G(raw_color) as u8, // fn drop(&mut self) {
lvgl_sys::_LV_COLOR_GET_B(raw_color) as u8, // unimplemented!()
) // }
} // }
}
}
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,
)
}
}
}
unsafe extern "C" fn display_callback_wrapper<T, C>( unsafe extern "C" fn display_callback_wrapper<T, C>(
disp_drv: *mut lvgl_sys::lv_disp_drv_t, 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, color_p: *mut lvgl_sys::lv_color_t,
) where ) where
T: DrawTarget<C>, T: DrawTarget<C>,
C: PixelColor + From<ColorRgb> C: PixelColor + From<Color>,
{ {
// We need to make sure panics can't escape across the FFI boundary. // We need to make sure panics can't escape across the FFI boundary.
//let _ = std::panic::catch_unwind(|| { //let _ = std::panic::catch_unwind(|| {
@ -121,10 +100,13 @@ unsafe extern "C" fn display_callback_wrapper<T, C>(
//let image_buffer = //let image_buffer =
for y in (*area).y1..=(*area).y2 { for y in (*area).y1..=(*area).y2 {
for x in (*area).x1..=(*area).x2 { 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; i = i + 1;
// TODO: Use device.draw_iter // 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); lvgl_sys::lv_disp_flush_ready(disp_drv);
//}); // end of panic::catch_unwind //}); // 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)
}

68
lvgl/src/global.rs Normal file
View file

@ -0,0 +1,68 @@
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,
}
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();
}
}
}

View file

@ -1,7 +1,12 @@
#![no_std] #![no_std]
pub mod display; extern crate alloc;
mod objx;
mod global;
mod display;
mod support;
mod widgets;
pub use global::{UI, LvError};
pub use display::DisplayDriver; pub use display::DisplayDriver;
pub use objx::*; pub use support::*;

View file

@ -1,30 +1,31 @@
use alloc::boxed::Box;
use core::mem; use core::mem;
use core::ptr; use core::ptr;
use cty; use cstr_core::CString;
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
use lvgl_sys; use lvgl_sys;
pub trait NativeObject { pub trait NativeObject {
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t>; 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>, raw: ptr::NonNull<lvgl_sys::lv_obj_t>,
style: Option<&'a mut Style>,
} }
impl<'a> ObjectX<'a> { impl ObjectX {
pub(crate) fn new(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self { pub(crate) fn from_raw(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
Self { raw, style: None } Self { raw }
} }
} }
impl<'a> NativeObject for ObjectX<'a> { impl NativeObject for ObjectX {
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> { fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
unsafe { ptr::NonNull::new_unchecked(self.raw.as_ptr()) } 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) { fn set_pos(&mut self, x: i16, y: i16) {
unsafe { unsafe {
lvgl_sys::lv_obj_set_pos( 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> { impl Object for ObjectX {
fn set_style(&mut self, style: &'a mut Style) { fn set_style(&mut self, style: Style) {
unsafe { 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 { macro_rules! define_object {
($item:ident) => { ($item:ident) => {
pub struct $item<'a> { pub struct $item {
core: ObjectX<'a>, core: ObjectX,
} }
impl<'a> NativeObject for $item<'a> { impl NativeObject for $item {
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> { fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
self.core.raw() self.core.raw()
} }
} }
impl<'a> Object<'a> for $item<'a> { impl Object for $item {
fn set_style(&mut self, style: &'a mut Style) { fn set_style(&mut self, style: Style) {
unsafe { 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); define_object!(Button);
impl<'a> Button<'a> { impl Button {
pub fn new<C>(parent: &mut C) -> Self pub fn new<C>(parent: &mut C) -> Self
where where
C: NativeObject, C: NativeObject,
@ -141,7 +142,7 @@ impl<'a> Button<'a> {
let ptr = lvgl_sys::lv_btn_create(parent.raw().as_mut(), ptr::null_mut()); let ptr = lvgl_sys::lv_btn_create(parent.raw().as_mut(), ptr::null_mut());
ptr::NonNull::new_unchecked(ptr) ptr::NonNull::new_unchecked(ptr)
}; };
let core = ObjectX::new(raw); let core = ObjectX::from_raw(raw);
Self { core } Self { core }
} }
} }
@ -155,7 +156,7 @@ pub enum LabelAlign {
define_object!(Label); define_object!(Label);
impl<'a> Label<'a> { impl Label {
pub fn new<C>(parent: &mut C) -> Self pub fn new<C>(parent: &mut C) -> Self
where where
C: NativeObject, C: NativeObject,
@ -164,16 +165,14 @@ impl<'a> Label<'a> {
let ptr = lvgl_sys::lv_label_create(parent.raw().as_mut(), ptr::null_mut()); let ptr = lvgl_sys::lv_label_create(parent.raw().as_mut(), ptr::null_mut());
ptr::NonNull::new_unchecked(ptr) ptr::NonNull::new_unchecked(ptr)
}; };
let core = ObjectX::new(raw); let core = ObjectX::from_raw(raw);
Self { core } Self { core }
} }
pub fn set_text(&mut self, text: &str) { pub fn set_text(&mut self, text: &str) {
let text = CString::new(text).unwrap();
unsafe { unsafe {
lvgl_sys::lv_label_set_text( lvgl_sys::lv_label_set_text(self.core.raw().as_mut(), text.as_ptr());
self.core.raw().as_mut(),
text.as_ptr() as *const cty::c_char,
);
} }
} }
@ -233,10 +232,6 @@ impl Style {
pub fn set_text_font(&mut self, font: &lvgl_sys::lv_font_t) { pub fn set_text_font(&mut self, font: &lvgl_sys::lv_font_t) {
self.raw.text.font = font; self.raw.text.font = font;
} }
fn raw(&mut self) -> *const lvgl_sys::lv_style_t {
&mut self.raw
}
} }
impl Clone for Style { impl Clone for Style {
@ -264,6 +259,34 @@ impl Color {
let raw = unsafe { lvgl_sys::_LV_COLOR_MAKE(r, g, b) }; let raw = unsafe { lvgl_sys::_LV_COLOR_MAKE(r, g, b) };
Self { raw } 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 { pub enum Align {

0
lvgl/src/widgets.rs Normal file
View file