Add LVGL Rust Global Allocator feature #48
16 changed files with 168 additions and 96 deletions
|
@ -1,7 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"lvgl",
|
"lvgl",
|
||||||
"lvgl-sys",
|
|
||||||
"lvgl-codegen",
|
"lvgl-codegen",
|
||||||
"examples"
|
"lvgl-sys",
|
||||||
]
|
]
|
||||||
|
|
24
README.md
24
README.md
|
@ -1,18 +1,19 @@
|
||||||
<h1 align="center"> LittlevGL - Open-source Embedded GUI Library in Rust</h1>
|
<h1 align="center"> LVGL - Open-source Embedded GUI Library in Rust</h1>
|
||||||
|
|
||||||
![Original LittlevGL demo image](lv_demo.png)
|
![Original LVGL demo image](lv_demo.png)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
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.
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
LittlevGL is compatible with <samp>#![no_std]</samp> environments by default.
|
LVGL is compatible with <samp>#![no_std]</samp> environments by default.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4 align="center">
|
<h4 align="center">
|
||||||
<a href="https://lvgl.io/">Official LittlevGL Website </a> ·
|
<a href="https://github.com/rafaelcaricio/lvgl-rs-wasm">Rust to WASM demo</a> ·
|
||||||
|
<a href="https://lvgl.io/">Official LVGL Website </a> ·
|
||||||
<a href="https://github.com/littlevgl/lvgl">C library repository</a> ·
|
<a href="https://github.com/littlevgl/lvgl">C library repository</a> ·
|
||||||
<a href="https://lvgl.io/demos">Live demo</a>
|
<a href="https://lvgl.io/demos">Official live demos</a>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -42,7 +43,7 @@ $ cargo add lvgl
|
||||||
|
|
||||||
The build requires the environment variable bellow to be set:
|
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:
|
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
|
```shell script
|
||||||
|
@ -58,10 +59,17 @@ for `no_std`, so we need to use a workaround to build "lvgl-rs". The mainstrem i
|
||||||
```shell
|
```shell
|
||||||
$ DEP_LV_CONFIG_PATH=`pwd` cargo build -Zfeatures=build_dep
|
$ 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
|
## 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
|
```shell
|
||||||
$ brew install sdl2
|
$ brew install sdl2
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "demo"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Rafael Caricio <crates@caric.io>"]
|
|
||||||
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"
|
|
|
@ -11,7 +11,31 @@ use lvgl::{LvError, Widget};
|
||||||
use lvgl_sys;
|
use lvgl_sys;
|
||||||
use std::time::Instant;
|
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> {
|
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<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
lvgl_sys::LV_HOR_RES_MAX,
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
lvgl_sys::LV_VER_RES_MAX,
|
||||||
|
@ -58,6 +82,7 @@ fn main() -> Result<(), LvError> {
|
||||||
if i > 270 {
|
if i > 270 {
|
||||||
forward = if forward { false } else { true };
|
forward = if forward { false } else { true };
|
||||||
i = 1;
|
i = 1;
|
||||||
|
println!("meminfo running: {:?}", mem_info());
|
||||||
}
|
}
|
||||||
angle = if forward { angle + 1 } else { angle - 1 };
|
angle = if forward { angle + 1 } else { angle - 1 };
|
||||||
arc.set_end_angle(angle + 135)?;
|
arc.set_end_angle(angle + 135)?;
|
||||||
|
|
|
@ -88,7 +88,7 @@ fn main() -> Result<(), LvError> {
|
||||||
Ok(())
|
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.
|
// TODO: Create a macro for defining a safe wrapper for fonts.
|
||||||
// Maybe sometihng like:
|
// Maybe sometihng like:
|
||||||
//
|
//
|
||||||
|
|
|
@ -80,7 +80,7 @@ typedef int16_t lv_coord_t;
|
||||||
#define LV_MEM_CUSTOM 0
|
#define LV_MEM_CUSTOM 0
|
||||||
#if LV_MEM_CUSTOM == 0
|
#if LV_MEM_CUSTOM == 0
|
||||||
/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
|
/* 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 */
|
/* Complier prefix for a big array declaration */
|
||||||
# define LV_MEM_ATTR
|
# define LV_MEM_ATTR
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lvgl-sys"
|
name = "lvgl-sys"
|
||||||
description = "Raw bindings to the LittlevGL C library."
|
description = "Raw bindings to the LVGL C library."
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
authors = ["Rafael Caricio <crates.lvgl-sys@caric.io>"]
|
authors = ["Rafael Caricio <crates.lvgl-sys@caric.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
|
@ -5,7 +5,7 @@ Rust raw bindings for LittlevGL library.
|
||||||
|
|
||||||
Build requires environment variables to be set:
|
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:
|
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
|
```shell script
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lvgl"
|
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"
|
version = "0.5.2"
|
||||||
authors = ["Rafael Caricio <crates.lvgl@caric.io>"]
|
authors = ["Rafael Caricio <crates.lvgl@caric.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -18,9 +18,40 @@ embedded-graphics = "0.6.2"
|
||||||
cstr_core = "0.2.3"
|
cstr_core = "0.2.3"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = ["cstr_core/alloc"]
|
||||||
|
lvgl_alloc = ["alloc"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
quote = "1.0.9"
|
quote = "1.0.9"
|
||||||
proc-macro2 = "1.0.24"
|
proc-macro2 = "1.0.24"
|
||||||
lvgl-codegen = { version = "0.5.2", path = "../lvgl-codegen" }
|
lvgl-codegen = { version = "0.5.2", path = "../lvgl-codegen" }
|
||||||
lvgl-sys = { version = "0.5.2", path = "../lvgl-sys" }
|
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"]
|
||||||
|
|
20
lvgl/src/allocator.rs
Normal file
20
lvgl/src/allocator.rs
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,13 +17,52 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
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 support;
|
||||||
mod ui;
|
mod ui;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod lv_core;
|
mod lv_core;
|
||||||
pub mod widgets;
|
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 lv_core::*;
|
||||||
pub use support::*;
|
pub use support::*;
|
||||||
pub use ui::*;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::lv_core::style::Style;
|
use crate::lv_core::style::Style;
|
||||||
use crate::mem::Box;
|
use crate::Box;
|
||||||
use crate::{Align, LvError, LvResult};
|
use crate::{Align, LvError, LvResult};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
/// Represents a native LittlevGL object
|
/// Represents a native LVGL object
|
||||||
pub trait NativeObject {
|
pub trait NativeObject {
|
||||||
/// Provide common way to access to the underlying native object pointer.
|
/// Provide common way to access to the underlying native object pointer.
|
||||||
fn raw(&self) -> LvResult<ptr::NonNull<lvgl_sys::lv_obj_t>>;
|
fn raw(&self) -> LvResult<ptr::NonNull<lvgl_sys::lv_obj_t>>;
|
||||||
|
@ -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 {
|
pub trait Widget: NativeObject {
|
||||||
type SpecialEvent;
|
type SpecialEvent;
|
||||||
type Part: Into<u8>;
|
type Part: Into<u8>;
|
||||||
|
@ -146,8 +146,8 @@ macro_rules! define_object {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut raw = self.raw()?;
|
let mut raw = self.raw()?;
|
||||||
let obj = raw.as_mut();
|
let obj = raw.as_mut();
|
||||||
let user_closure = $crate::mem::Box::new(f)?;
|
let user_closure = $crate::Box::new(f);
|
||||||
obj.user_data = $crate::mem::Box::into_raw(user_closure) as *mut cty::c_void;
|
obj.user_data = $crate::Box::into_raw(user_closure) as *mut cty::c_void;
|
||||||
lvgl_sys::lv_obj_set_event_cb(
|
lvgl_sys::lv_obj_set_event_cb(
|
||||||
obj,
|
obj,
|
||||||
lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::<Self, F>),
|
lvgl_sys::lv_event_cb_t::Some($crate::support::event_callback::<Self, F>),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::mem::Box;
|
use crate::Box;
|
||||||
use crate::{Color, LvResult, State};
|
use crate::{Color, LvResult, State};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use cstr_core::CStr;
|
use cstr_core::CStr;
|
||||||
|
@ -31,7 +31,7 @@ impl Default for Style {
|
||||||
let raw = unsafe {
|
let raw = unsafe {
|
||||||
let mut style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
let mut style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
||||||
lvgl_sys::lv_style_init(style.as_mut_ptr());
|
lvgl_sys::lv_style_init(style.as_mut_ptr());
|
||||||
Box::new(style.assume_init()).unwrap()
|
Box::new(style.assume_init())
|
||||||
};
|
};
|
||||||
Self { raw }
|
Self { raw }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::{LvError, LvResult};
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
@ -12,7 +11,7 @@ pub(crate) struct Box<T>(NonNull<T>);
|
||||||
|
|
||||||
impl<T> Box<T> {
|
impl<T> Box<T> {
|
||||||
/// Allocate memory using LVGL memory API and place `T` in the LVGL tracked memory.
|
/// Allocate memory using LVGL memory API and place `T` in the LVGL tracked memory.
|
||||||
pub fn new(value: T) -> LvResult<Box<T>> {
|
pub fn new(value: T) -> Box<T> {
|
||||||
let size = mem::size_of::<T>();
|
let size = mem::size_of::<T>();
|
||||||
let inner = unsafe {
|
let inner = unsafe {
|
||||||
let ptr = lvgl_sys::lv_mem_alloc(size as lvgl_sys::size_t) as *mut T;
|
let ptr = lvgl_sys::lv_mem_alloc(size as lvgl_sys::size_t) as *mut T;
|
||||||
|
@ -29,9 +28,11 @@ impl<T> Box<T> {
|
||||||
p.as_ptr().write(value);
|
p.as_ptr().write(value);
|
||||||
p
|
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 {
|
pub fn into_raw(self) -> *mut T {
|
||||||
|
@ -71,32 +72,21 @@ impl<T> AsMut<T> for Box<T> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::Once;
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
static INIT_LVGL: Once = Once::new();
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
INIT_LVGL.call_once(|| {
|
|
||||||
unsafe {
|
|
||||||
lvgl_sys::lv_init();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn place_value_in_lv_mem() {
|
fn place_value_in_lv_mem() {
|
||||||
init();
|
crate::lvgl_init();
|
||||||
|
|
||||||
let v = Box::new(5).unwrap();
|
let v = Box::new(5);
|
||||||
drop(v);
|
drop(v);
|
||||||
let v = Box::new(10).unwrap();
|
let v = Box::new(10);
|
||||||
drop(v);
|
drop(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn place_complex_value_in_lv_mem() {
|
fn place_complex_value_in_lv_mem() {
|
||||||
init();
|
crate::lvgl_init();
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -119,11 +109,7 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{:?}", p);
|
println!("{:?}", p);
|
||||||
let mut b = Box::new(p).unwrap_or_else(|_| {
|
let mut b = Box::new(p);
|
||||||
let info = mem_info();
|
|
||||||
println!("mem info: {:?}", &info);
|
|
||||||
panic!("OOM");
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("memory address is {:p}", b.as_mut());
|
println!("memory address is {:p}", b.as_mut());
|
||||||
|
|
||||||
|
@ -159,7 +145,7 @@ mod test {
|
||||||
used_cnt: 0,
|
used_cnt: 0,
|
||||||
max_used: 0,
|
max_used: 0,
|
||||||
used_pct: 0,
|
used_pct: 0,
|
||||||
frag_pct: 0
|
frag_pct: 0,
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
lvgl_sys::lv_mem_monitor(&mut info as *mut _);
|
lvgl_sys::lv_mem_monitor(&mut info as *mut _);
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl From<Color> 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:
|
/// the user, e.g. if an object:
|
||||||
/// - is clicked
|
/// - is clicked
|
||||||
/// - is dragged
|
/// - is dragged
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::mem::Box;
|
use crate::Box;
|
||||||
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
@ -10,7 +10,7 @@ use embedded_graphics::pixelcolor::PixelColor;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
use embedded_graphics::{drawable, DrawTarget};
|
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);
|
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
// TODO: Make this an external configuration
|
// TODO: Make this an external configuration
|
||||||
|
@ -23,13 +23,13 @@ where
|
||||||
T: DrawTarget<C>,
|
T: DrawTarget<C>,
|
||||||
C: PixelColor + From<Color>,
|
C: PixelColor + From<Color>,
|
||||||
{
|
{
|
||||||
// LittlevGL is not thread-safe by default.
|
// LVGL is not thread-safe by default.
|
||||||
_not_sync: PhantomData<*mut ()>,
|
_not_sync: PhantomData<*mut ()>,
|
||||||
// Later we can add possibility to have multiple displays by using `heapless::Vec`
|
// Later we can add possibility to have multiple displays by using `heapless::Vec`
|
||||||
display_data: Option<DisplayUserData<T, C>>,
|
display_data: Option<DisplayUserData<T, C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// LittlevGL does not use thread locals.
|
// LVGL does not use thread locals.
|
||||||
unsafe impl<T, C> Send for UI<T, C>
|
unsafe impl<T, C> Send for UI<T, C>
|
||||||
where
|
where
|
||||||
T: DrawTarget<C>,
|
T: DrawTarget<C>,
|
||||||
|
@ -47,9 +47,7 @@ where
|
||||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
unsafe {
|
crate::lvgl_init();
|
||||||
lvgl_sys::lv_init();
|
|
||||||
}
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_not_sync: PhantomData,
|
_not_sync: PhantomData,
|
||||||
display_data: None,
|
display_data: None,
|
||||||
|
@ -75,15 +73,15 @@ where
|
||||||
// Initialize the display buffer
|
// Initialize the display buffer
|
||||||
lvgl_sys::lv_disp_buf_init(
|
lvgl_sys::lv_disp_buf_init(
|
||||||
disp_buf.as_mut_ptr(),
|
disp_buf.as_mut_ptr(),
|
||||||
Box::into_raw(Box::new(refresh_buffer1)?) 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,
|
Box::into_raw(Box::new(refresh_buffer2)) as *mut cty::c_void,
|
||||||
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
||||||
);
|
);
|
||||||
// Basic initialization of the display driver
|
// Basic initialization of the display driver
|
||||||
lvgl_sys::lv_disp_drv_init(disp_drv.as_mut_ptr());
|
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
|
// 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
|
// Set your driver function
|
||||||
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>);
|
disp_drv.flush_cb = Some(display_callback_wrapper::<T, C>);
|
||||||
disp_drv.user_data = &mut self.display_data as *mut _ as *mut cty::c_void;
|
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<T, C>(
|
||||||
// TODO: Can we do anything when there is a error while flushing?
|
// 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);
|
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);
|
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.
|
// 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.
|
// what is the lvgl-rs wrapper responsibility.
|
||||||
fn display_flush<T, C>(
|
fn display_flush<T, C>(
|
||||||
display: &mut T,
|
display: &mut T,
|
||||||
|
|
Loading…
Reference in a new issue
We could enable the LVGL side to use Rust memory allocator, if it's available and the feature "alloc" enabled in lvgl-rs. That way we wouldn't need to implement this custom
Box
implementation for that case. We would still need this implementation for cases where all features in lvgl-rs are disabled and we cannot assume that LVGL is using the same memory space.The problem is that LVGL C always support "dynamically" allocated memory backed by a static array-based backend, just like wee_alloc. LVGL-rs is a library and the choice of allocator must be made by the firmware/application developer. Besides that, the developer might decide that they don't want to have an allocator at all on their Rust firmware. LVGL C will still use their own memory management implementation.
One option is to always require users of LVGL-rs to define a global allocator in their project. This would be a limitation for some use cases?! In that scenario, we wouldn't need the code in
mem
module at all, since the memory space would be shared by LVGL C and Rust.