diff --git a/Cargo.toml b/Cargo.toml
index 4f3767e..06b993d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,6 @@
[workspace]
members = [
"lvgl",
- "lvgl-sys",
"lvgl-codegen",
- "examples"
+ "lvgl-sys",
]
diff --git a/README.md b/README.md
index 348843b..355d2a9 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,19 @@
-
LittlevGL - Open-source Embedded GUI Library in Rust
+ LVGL - Open-source Embedded GUI Library in Rust
-![Original LittlevGL demo image](lv_demo.png)
+![Original LVGL demo image](lv_demo.png)
-LittlevGL provides everything you need to create a Graphical User Interface (GUI) on embedded systems with easy-to-use graphical elements, beautiful visual effects and low memory footprint.
+LVGL provides everything you need to create a Graphical User Interface (GUI) on embedded systems with easy-to-use graphical elements, beautiful visual effects and low memory footprint.
-LittlevGL is compatible with #![no_std] environments by default.
+LVGL is compatible with #![no_std] environments by default.
---
@@ -42,7 +43,7 @@ $ cargo add lvgl
The build requires the environment variable bellow to be set:
-- `DEP_LV_CONFIG_PATH`: Path to the directory containing the `lv_conf.h` header file used for configuration of LittlevGL library.
+- `DEP_LV_CONFIG_PATH`: Path to the directory containing the `lv_conf.h` header file used for configuration of LVGL library.
We recommend the `lv_conf.h` file to be in your project's root directory. If so, the command to build your project would be:
```shell script
@@ -58,10 +59,17 @@ for `no_std`, so we need to use a workaround to build "lvgl-rs". The mainstrem i
```shell
$ DEP_LV_CONFIG_PATH=`pwd` cargo build -Zfeatures=build_dep
```
+### LVGL Global Allocator
+
+A [global allocator](https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html) for Rust leveraging the
+[LVGL memory allocator](https://github.com/lvgl/lvgl/blob/master/src/misc/lv_mem.h) is provided, but not enabled
+by default. Can be enabled by the feature `lvgl_alloc`. This will make all dynamic memory to be allocated by LVGL
+internal memory manager.
## Running the demo
-**Hint for macOS users**: Before you run the demos you need to make sure you have [libsdl](https://www.libsdl.org) installed on your machine. To install it, use HomeBrew:
+**Hint for macOS users**: Before you run the demos you need to make sure you have [libsdl](https://www.libsdl.org)
+installed on your machine. To install it, use HomeBrew:
```shell
$ brew install sdl2
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
deleted file mode 100644
index 8d2efac..0000000
--- a/examples/Cargo.toml
+++ /dev/null
@@ -1,34 +0,0 @@
-[package]
-name = "demo"
-version = "0.1.0"
-authors = ["Rafael Caricio "]
-edition = "2018"
-publish = false
-
-[dev-dependencies]
-lvgl = { path = "../lvgl" }
-lvgl-sys = { path = "../lvgl-sys" }
-embedded-graphics = "0.6.2"
-embedded-graphics-simulator = "0.2.1"
-heapless = "0.5.5"
-cstr_core = { version = "0.2.0", features = ["alloc"] }
-
-[[example]]
-name = "demo"
-path = "demo.rs"
-
-[[example]]
-name = "bar"
-path = "bar.rs"
-
-[[example]]
-name = "button_click"
-path = "button_click.rs"
-
-[[example]]
-name = "gauge"
-path = "gauge.rs"
-
-[[example]]
-name = "arc"
-path = "arc.rs"
diff --git a/examples/arc.rs b/examples/arc.rs
index 05523a4..eb2a1f5 100644
--- a/examples/arc.rs
+++ b/examples/arc.rs
@@ -11,7 +11,31 @@ use lvgl::{LvError, Widget};
use lvgl_sys;
use std::time::Instant;
+fn mem_info() -> lvgl_sys::lv_mem_monitor_t {
+ let mut info = lvgl_sys::lv_mem_monitor_t {
+ total_size: 0,
+ free_cnt: 0,
+ free_size: 0,
+ free_biggest_size: 0,
+ used_cnt: 0,
+ max_used: 0,
+ used_pct: 0,
+ frag_pct: 0,
+ };
+ unsafe {
+ lvgl_sys::lv_mem_monitor(&mut info as *mut _);
+ }
+ info
+}
+
fn main() -> Result<(), LvError> {
+ println!("meminfo init: {:?}", mem_info());
+ run_arc_demo()?;
+ println!("meminfo end: {:?}", mem_info());
+ Ok(())
+}
+
+fn run_arc_demo() -> Result<(), LvError> {
let display: SimulatorDisplay = SimulatorDisplay::new(Size::new(
lvgl_sys::LV_HOR_RES_MAX,
lvgl_sys::LV_VER_RES_MAX,
@@ -58,6 +82,7 @@ fn main() -> Result<(), LvError> {
if i > 270 {
forward = if forward { false } else { true };
i = 1;
+ println!("meminfo running: {:?}", mem_info());
}
angle = if forward { angle + 1 } else { angle - 1 };
arc.set_end_angle(angle + 135)?;
diff --git a/examples/demo.rs b/examples/demo.rs
index 956e4fc..d77f732 100644
--- a/examples/demo.rs
+++ b/examples/demo.rs
@@ -88,7 +88,7 @@ fn main() -> Result<(), LvError> {
Ok(())
}
-// Reference to native font for LittlevGL, defined in the file: "fonts_noto_sans_numeric_80.c"
+// Reference to native font for LVGL, defined in the file: "fonts_noto_sans_numeric_80.c"
// TODO: Create a macro for defining a safe wrapper for fonts.
// Maybe sometihng like:
//
diff --git a/examples/include/lv_conf.h b/examples/include/lv_conf.h
index 6fe446c..d6be99d 100644
--- a/examples/include/lv_conf.h
+++ b/examples/include/lv_conf.h
@@ -80,7 +80,7 @@ typedef int16_t lv_coord_t;
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
-# define LV_MEM_SIZE (32U * 1024U)
+# define LV_MEM_SIZE (1048576U) // 1Mb
/* Complier prefix for a big array declaration */
# define LV_MEM_ATTR
diff --git a/lvgl-sys/Cargo.toml b/lvgl-sys/Cargo.toml
index b50b140..ba7b648 100644
--- a/lvgl-sys/Cargo.toml
+++ b/lvgl-sys/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lvgl-sys"
-description = "Raw bindings to the LittlevGL C library."
+description = "Raw bindings to the LVGL C library."
version = "0.5.2"
authors = ["Rafael Caricio "]
edition = "2018"
diff --git a/lvgl-sys/README.md b/lvgl-sys/README.md
index 0d8a775..5c3ab3f 100644
--- a/lvgl-sys/README.md
+++ b/lvgl-sys/README.md
@@ -5,7 +5,7 @@ Rust raw bindings for LittlevGL library.
Build requires environment variables to be set:
-- `DEP_LV_CONFIG_PATH`: Path to the directory containing the `lv_conf.h` header file used for configuration of LittlevGL library.
+- `DEP_LV_CONFIG_PATH`: Path to the directory containing the `lv_conf.h` header file used for configuration of LVGL library.
We recommend the `lv_conf.h` file to be in your project's root directory. If so, the command to build your project would be:
```shell script
diff --git a/lvgl/Cargo.toml b/lvgl/Cargo.toml
index e460295..dfa8971 100644
--- a/lvgl/Cargo.toml
+++ b/lvgl/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lvgl"
-description = "LittlevGL bindings for Rust. A powerful and easy-to-use embedded GUI with many widgets, advanced visual effects (opacity, antialiasing, animations) and low memory requirements (16K RAM, 64K Flash)."
+description = "LVGL bindings for Rust. A powerful and easy-to-use embedded GUI with many widgets, advanced visual effects (opacity, antialiasing, animations) and low memory requirements (16K RAM, 64K Flash)."
version = "0.5.2"
authors = ["Rafael Caricio "]
edition = "2018"
@@ -18,9 +18,40 @@ embedded-graphics = "0.6.2"
cstr_core = "0.2.3"
bitflags = "1.2.1"
+[features]
+alloc = ["cstr_core/alloc"]
+lvgl_alloc = ["alloc"]
+
[build-dependencies]
quote = "1.0.9"
proc-macro2 = "1.0.24"
lvgl-codegen = { version = "0.5.2", path = "../lvgl-codegen" }
lvgl-sys = { version = "0.5.2", path = "../lvgl-sys" }
+[dev-dependencies]
+embedded-graphics-simulator = "0.2.1"
+heapless = "0.5.5"
+
+[[example]]
+name = "demo"
+path = "../examples/demo.rs"
+required-features = ["alloc"]
+
+[[example]]
+name = "bar"
+path = "../examples/bar.rs"
+required-features = ["alloc"]
+
+[[example]]
+name = "button_click"
+path = "../examples/button_click.rs"
+required-features = ["alloc"]
+
+[[example]]
+name = "gauge"
+path = "../examples/gauge.rs"
+
+[[example]]
+name = "arc"
+path = "../examples/arc.rs"
+required-features = ["lvgl_alloc"]
diff --git a/lvgl/src/allocator.rs b/lvgl/src/allocator.rs
new file mode 100644
index 0000000..90e60fd
--- /dev/null
+++ b/lvgl/src/allocator.rs
@@ -0,0 +1,20 @@
+use core::alloc::{GlobalAlloc, Layout};
+
+// Register the global allocator
+#[global_allocator]
+static ALLOCATOR: LvglAlloc = LvglAlloc;
+
+pub struct LvglAlloc;
+
+unsafe impl GlobalAlloc for LvglAlloc {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // Make sure LVGL is initialized!
+ crate::lvgl_init();
+ lvgl_sys::lv_mem_alloc(layout.size() as lvgl_sys::size_t) as *mut u8
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ crate::lvgl_init();
+ lvgl_sys::lv_mem_free(ptr as *const cty::c_void)
+ }
+}
diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs
index 8a4a17f..e46452c 100644
--- a/lvgl/src/lib.rs
+++ b/lvgl/src/lib.rs
@@ -17,13 +17,52 @@
#[macro_use]
extern crate bitflags;
-pub(crate) mod mem;
+#[cfg(feature = "lvgl_alloc")]
+extern crate alloc;
+
+// We can ONLY use `alloc::boxed::Box` if `lvgl_alloc` is enabled.
+// That is because we use `Box` to send memory references to LVGL. Since the global allocator, when
+// `lvgl_alloc` feature is enabled, is the LVGL memory manager then everything is in LVGL
+// managed memory anyways. In that case we can use the Rust's provided Box definition.
+//
+#[cfg(feature = "lvgl_alloc")]
+use ::alloc::boxed::Box;
+
+#[cfg(feature = "lvgl_alloc")]
+mod allocator;
+
mod support;
mod ui;
#[macro_use]
mod lv_core;
pub mod widgets;
+#[cfg(not(feature = "lvgl_alloc"))]
+pub(crate) mod mem;
+
+// When LVGL allocator is not used on the Rust code, we need a way to add objects to the LVGL
+// managed memory. We implement a very simple `Box` that has the minimal features to copy memory
+// safely to the LVGL managed memory.
+//
+#[cfg(not(feature = "lvgl_alloc"))]
+use crate::mem::Box;
+
pub use lv_core::*;
pub use support::*;
pub use ui::*;
+
+use core::sync::atomic::{AtomicBool, Ordering};
+
+// Initialize LVGL only once.
+static LVGL_INITIALIZED: AtomicBool = AtomicBool::new(false);
+
+pub(crate) fn lvgl_init() {
+ if LVGL_INITIALIZED
+ .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ unsafe {
+ lvgl_sys::lv_init();
+ }
+ }
+}
diff --git a/lvgl/src/lv_core/obj.rs b/lvgl/src/lv_core/obj.rs
index 1129c0a..d3ce4f6 100644
--- a/lvgl/src/lv_core/obj.rs
+++ b/lvgl/src/lv_core/obj.rs
@@ -1,9 +1,9 @@
use crate::lv_core::style::Style;
-use crate::mem::Box;
+use crate::Box;
use crate::{Align, LvError, LvResult};
use core::ptr;
-/// Represents a native LittlevGL object
+/// Represents a native LVGL object
pub trait NativeObject {
/// Provide common way to access to the underlying native object pointer.
fn raw(&self) -> LvResult>;
@@ -28,7 +28,7 @@ impl NativeObject for Obj {
}
}
-/// A wrapper for all LittlevGL common operations on generic objects.
+/// A wrapper for all LVGL common operations on generic objects.
pub trait Widget: NativeObject {
type SpecialEvent;
type Part: Into;
@@ -146,8 +146,8 @@ macro_rules! define_object {
unsafe {
let mut raw = self.raw()?;
let obj = raw.as_mut();
- let user_closure = $crate::mem::Box::new(f)?;
- obj.user_data = $crate::mem::Box::into_raw(user_closure) as *mut cty::c_void;
+ let user_closure = $crate::Box::new(f);
+ obj.user_data = $crate::Box::into_raw(user_closure) as *mut cty::c_void;
lvgl_sys::lv_obj_set_event_cb(
obj,
lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::),
diff --git a/lvgl/src/lv_core/style.rs b/lvgl/src/lv_core/style.rs
index ace0c46..46611b8 100644
--- a/lvgl/src/lv_core/style.rs
+++ b/lvgl/src/lv_core/style.rs
@@ -1,4 +1,4 @@
-use crate::mem::Box;
+use crate::Box;
use crate::{Color, LvResult, State};
use core::mem;
use cstr_core::CStr;
@@ -31,7 +31,7 @@ impl Default for Style {
let raw = unsafe {
let mut style = mem::MaybeUninit::::uninit();
lvgl_sys::lv_style_init(style.as_mut_ptr());
- Box::new(style.assume_init()).unwrap()
+ Box::new(style.assume_init())
};
Self { raw }
}
diff --git a/lvgl/src/mem.rs b/lvgl/src/mem.rs
index 55ca9f6..0d193d7 100644
--- a/lvgl/src/mem.rs
+++ b/lvgl/src/mem.rs
@@ -1,4 +1,3 @@
-use crate::{LvError, LvResult};
use core::mem;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
@@ -12,7 +11,7 @@ pub(crate) struct Box(NonNull);
impl Box {
/// Allocate memory using LVGL memory API and place `T` in the LVGL tracked memory.
- pub fn new(value: T) -> LvResult> {
+ pub fn new(value: T) -> Box {
let size = mem::size_of::();
let inner = unsafe {
let ptr = lvgl_sys::lv_mem_alloc(size as lvgl_sys::size_t) as *mut T;
@@ -29,9 +28,11 @@ impl Box {
p.as_ptr().write(value);
p
})
- .ok_or(LvError::LvOOMemory)?
+ .unwrap_or_else(|| {
+ panic!("Could not allocate memory {} bytes", size);
+ })
};
- Ok(Box(inner))
+ Box(inner)
}
pub fn into_raw(self) -> *mut T {
@@ -71,32 +72,21 @@ impl AsMut for Box {
#[cfg(test)]
mod test {
use super::*;
- use std::sync::Once;
use std::vec::Vec;
- static INIT_LVGL: Once = Once::new();
-
- fn init() {
- INIT_LVGL.call_once(|| {
- unsafe {
- lvgl_sys::lv_init();
- };
- });
- }
-
#[test]
fn place_value_in_lv_mem() {
- init();
+ crate::lvgl_init();
- let v = Box::new(5).unwrap();
+ let v = Box::new(5);
drop(v);
- let v = Box::new(10).unwrap();
+ let v = Box::new(10);
drop(v);
}
#[test]
fn place_complex_value_in_lv_mem() {
- init();
+ crate::lvgl_init();
#[repr(C)]
#[derive(Debug)]
@@ -119,11 +109,7 @@ mod test {
};
println!("{:?}", p);
- let mut b = Box::new(p).unwrap_or_else(|_| {
- let info = mem_info();
- println!("mem info: {:?}", &info);
- panic!("OOM");
- });
+ let mut b = Box::new(p);
println!("memory address is {:p}", b.as_mut());
@@ -159,7 +145,7 @@ mod test {
used_cnt: 0,
max_used: 0,
used_pct: 0,
- frag_pct: 0
+ frag_pct: 0,
};
unsafe {
lvgl_sys::lv_mem_monitor(&mut info as *mut _);
diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs
index 1d529a7..c3861ae 100644
--- a/lvgl/src/support.rs
+++ b/lvgl/src/support.rs
@@ -65,7 +65,7 @@ impl From for Rgb565 {
}
}
-/// Events are triggered in LittlevGL when something happens which might be interesting to
+/// Events are triggered in LVGL when something happens which might be interesting to
/// the user, e.g. if an object:
/// - is clicked
/// - is dragged
diff --git a/lvgl/src/ui.rs b/lvgl/src/ui.rs
index 4a96f90..b6190ed 100644
--- a/lvgl/src/ui.rs
+++ b/lvgl/src/ui.rs
@@ -1,4 +1,4 @@
-use crate::mem::Box;
+use crate::Box;
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
@@ -10,7 +10,7 @@ use embedded_graphics::pixelcolor::PixelColor;
use embedded_graphics::prelude::*;
use embedded_graphics::{drawable, DrawTarget};
-// There can only be a single reference to LittlevGL library.
+// There can only be a single reference to LVGL library.
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
// TODO: Make this an external configuration
@@ -23,13 +23,13 @@ where
T: DrawTarget,
C: PixelColor + From,
{
- // LittlevGL is not thread-safe by default.
+ // LVGL is not thread-safe by default.
_not_sync: PhantomData<*mut ()>,
// Later we can add possibility to have multiple displays by using `heapless::Vec`
display_data: Option>,
}
-// LittlevGL does not use thread locals.
+// LVGL does not use thread locals.
unsafe impl Send for UI
where
T: DrawTarget,
@@ -47,9 +47,7 @@ where
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
- unsafe {
- lvgl_sys::lv_init();
- }
+ crate::lvgl_init();
Ok(Self {
_not_sync: PhantomData,
display_data: None,
@@ -75,15 +73,15 @@ where
// Initialize the display buffer
lvgl_sys::lv_disp_buf_init(
disp_buf.as_mut_ptr(),
- Box::into_raw(Box::new(refresh_buffer1)?) as *mut cty::c_void,
- Box::into_raw(Box::new(refresh_buffer2)?) as *mut cty::c_void,
+ Box::into_raw(Box::new(refresh_buffer1)) as *mut cty::c_void,
+ Box::into_raw(Box::new(refresh_buffer2)) as *mut cty::c_void,
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
);
// Basic initialization of the display driver
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
- let mut disp_drv = Box::new(disp_drv.assume_init())?;
+ let mut disp_drv = Box::new(disp_drv.assume_init());
// Assign the buffer to the display
- disp_drv.buffer = Box::into_raw(Box::new(disp_buf.assume_init())?);
+ disp_drv.buffer = Box::into_raw(Box::new(disp_buf.assume_init()));
// Set your driver function
disp_drv.flush_cb = Some(display_callback_wrapper::);
disp_drv.user_data = &mut self.display_data as *mut _ as *mut cty::c_void;
@@ -166,12 +164,12 @@ unsafe extern "C" fn display_callback_wrapper(
// TODO: Can we do anything when there is a error while flushing?
let _ = display_flush(&mut user_data.display, (x1, x2), (y1, y2), color_p);
}
- // Indicate to LittlevGL that we are ready with the flushing
+ // Indicate to LVGL that we are ready with the flushing
lvgl_sys::lv_disp_flush_ready(disp_drv);
}
// We separate this display flush function to reduce the amount of unsafe code we need to write.
-// This also provides a good separation of concerns, what is necessary from LittlevGL to work and
+// This also provides a good separation of concerns, what is necessary from LVGL to work and
// what is the lvgl-rs wrapper responsibility.
fn display_flush(
display: &mut T,