Allow to create statically allocated DrawBuffer
This commit is contained in:
parent
6ba1fcdaff
commit
00e2004822
7 changed files with 164 additions and 72 deletions
3
.github/workflows/rust.yml
vendored
3
.github/workflows/rust.yml
vendored
|
@ -30,4 +30,5 @@ jobs:
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --verbose
|
# LVGL is not thread safe, we need to run tests sequentially
|
||||||
|
run: cargo test --verbose -- --nocapture --test-threads 1
|
||||||
|
|
|
@ -5,7 +5,7 @@ use embedded_graphics_simulator::{
|
||||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||||
};
|
};
|
||||||
use lvgl;
|
use lvgl;
|
||||||
use lvgl::display::{DefaultDisplay, Display};
|
use lvgl::display::{DefaultDisplay, Display, DrawBuffer};
|
||||||
use lvgl::style::Style;
|
use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Label, LabelAlign};
|
use lvgl::widgets::{Label, LabelAlign};
|
||||||
use lvgl::{Align, Color, LvError, Part, State, Widget, UI};
|
use lvgl::{Align, Color, LvError, Part, State, Widget, UI};
|
||||||
|
@ -16,6 +16,8 @@ use std::thread;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
static DRAW_BUFFER: DrawBuffer = DrawBuffer::new();
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
lvgl::init();
|
lvgl::init();
|
||||||
|
|
||||||
|
@ -29,11 +31,13 @@ fn main() -> Result<(), LvError> {
|
||||||
|
|
||||||
// Implement and register your display:
|
// Implement and register your display:
|
||||||
let shared_native_display = SyncArc::new(Mutex::new(embedded_graphics_display));
|
let shared_native_display = SyncArc::new(Mutex::new(embedded_graphics_display));
|
||||||
let _display = Display::register_shared(&shared_native_display)?;
|
let _display = Display::register_shared(&DRAW_BUFFER, &shared_native_display)?;
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = DefaultDisplay::get_scr_act()?;
|
let mut screen = DefaultDisplay::get_scr_act()?;
|
||||||
|
|
||||||
|
println!("Before all widgets: {:?}", mem_info());
|
||||||
|
|
||||||
let mut screen_style = Style::default();
|
let mut screen_style = Style::default();
|
||||||
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||||
screen_style.set_radius(State::DEFAULT, 0);
|
screen_style.set_radius(State::DEFAULT, 0);
|
||||||
|
@ -92,9 +96,12 @@ fn main() -> Result<(), LvError> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!("During run: {:?}", mem_info());
|
||||||
sleep(Duration::from_secs(1));
|
sleep(Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Final part of demo app: {:?}", mem_info());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,3 +116,20 @@ fn main() -> Result<(), LvError> {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -3,23 +3,64 @@
|
||||||
/// This struct represents all relevant information we can extract from the C function declaration
|
/// 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
|
/// 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.
|
/// should be represented in a safe Rust API.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct CParameter {
|
pub struct CParameter {
|
||||||
/// The name of the parameter in the C code.
|
/// The name of the parameter in the C code.
|
||||||
name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// This is the raw representation of the Rust equivalent of the C type.
|
/// This is the raw representation of the Rust equivalent of the C type.
|
||||||
c_type: String,
|
pub c_type: String,
|
||||||
|
|
||||||
/// Takes a pointer to a type that is referenced by the LVGL code permanently.
|
/// Takes a pointer to a type that is referenced by the LVGL code permanently.
|
||||||
owned: bool,
|
pub scope: ParameterScope,
|
||||||
|
|
||||||
/// The pointer is not marked as `*const` so the referenced object can be mutated.
|
/// The pointer is not marked as `*const` so the referenced object can be mutated.
|
||||||
mutable: bool,
|
pub mutable: bool,
|
||||||
|
|
||||||
/// We need to check if the value is optional in the C code. We need to check
|
/// We need to check if the value is optional in the C code. We need to check
|
||||||
/// the function comments for this information.
|
/// the function comments for this information.
|
||||||
/// - "if NULL then"
|
/// - "if NULL then"
|
||||||
/// - "if not NULL then"
|
/// - "if not NULL then"
|
||||||
/// - "NULL to"
|
/// - "NULL to"
|
||||||
nullable: bool,
|
pub allow_none: bool,
|
||||||
|
|
||||||
|
/// Comment associated with the parameter, if exists.
|
||||||
|
pub comment: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ParameterScope {
|
||||||
|
Call,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum FunctionKind {
|
||||||
|
Constructor,
|
||||||
|
Method,
|
||||||
|
Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inference from a LVGL C API function.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Function {
|
||||||
|
/// Name of the function in the LVGL C API.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// Comment associated with the function, if exists.
|
||||||
|
pub comment: Option<String>,
|
||||||
|
|
||||||
|
pub kind: FunctionKind,
|
||||||
|
|
||||||
|
pub parameters: Vec<CParameter>,
|
||||||
|
|
||||||
|
pub ret: Return,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Return {
|
||||||
|
Value(Option<CParameter>),
|
||||||
|
|
||||||
|
/// If the return is a LVGL result
|
||||||
|
ResultError(CParameter),
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,7 +356,7 @@ impl Rusty for LvType {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let val = if self.is_str() {
|
let val = if self.is_str() {
|
||||||
quote!(&cstr_core::CStr)
|
quote!(&cstr_core::CStr)
|
||||||
} else if (self.literal_name.contains("lv_")) {
|
} else if self.literal_name.contains("lv_") {
|
||||||
let ident = format_ident!("{}", name);
|
let ident = format_ident!("{}", name);
|
||||||
quote!(&#ident)
|
quote!(&#ident)
|
||||||
} else {
|
} else {
|
||||||
|
@ -672,9 +672,8 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> crate::LvResult<Self> {
|
pub fn new() -> crate::LvResult<Self> {
|
||||||
Ok(Self::create_at(
|
let mut parent = crate::display::DefaultDisplay::get_scr_act()?;
|
||||||
&mut crate::display::DefaultDisplay::get_scr_act(),
|
Ok(Self::create_at(&mut parent)?)
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,7 @@ cstr_core = "0.2.3"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
heapless = "0.7.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["cstr_core/alloc"]
|
alloc = ["cstr_core/alloc"]
|
||||||
|
@ -32,7 +33,6 @@ lvgl-sys = { version = "0.5.2", path = "../lvgl-sys" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embedded-graphics-simulator = "0.2.1"
|
embedded-graphics-simulator = "0.2.1"
|
||||||
heapless = "0.5.5"
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "app"
|
name = "app"
|
||||||
|
|
|
@ -2,15 +2,16 @@ use crate::functions::CoreError;
|
||||||
use crate::Box;
|
use crate::Box;
|
||||||
use crate::{disp_drv_register, disp_get_default, get_str_act};
|
use crate::{disp_drv_register, disp_get_default, get_str_act};
|
||||||
use crate::{Color, Obj};
|
use crate::{Color, Obj};
|
||||||
|
use core::cell::{Cell, RefCell};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
use core::mem::MaybeUninit;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use core::{ptr, result};
|
use core::{ptr, result};
|
||||||
use embedded_graphics::drawable;
|
use embedded_graphics::drawable;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
|
use parking_lot::const_mutex;
|
||||||
#[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")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
|
@ -20,11 +21,6 @@ const REFRESH_BUFFER_LEN: usize = 2;
|
||||||
// Declare a buffer for the refresh rate
|
// Declare a buffer for the refresh rate
|
||||||
const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
|
const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
|
||||||
|
|
||||||
static mut REFRESH_BUFFER1: [MaybeUninit<lvgl_sys::lv_color_t>; BUF_SIZE] =
|
|
||||||
[MaybeUninit::<lvgl_sys::lv_color_t>::uninit(); BUF_SIZE];
|
|
||||||
|
|
||||||
static mut DRAW_BUFFER: MaybeUninit<lvgl_sys::lv_disp_buf_t> = MaybeUninit::uninit();
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum DisplayError {
|
pub enum DisplayError {
|
||||||
NotAvailable,
|
NotAvailable,
|
||||||
|
@ -46,23 +42,26 @@ impl Display {
|
||||||
Self { disp }
|
Self { disp }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register<T, C>(native_display: T) -> Result<Self>
|
pub fn register<T, C>(draw_buffer: &'static DrawBuffer, native_display: T) -> Result<Self>
|
||||||
where
|
where
|
||||||
T: DrawTarget<C>,
|
T: DrawTarget<C>,
|
||||||
C: PixelColor + From<Color>,
|
C: PixelColor + From<Color>,
|
||||||
{
|
{
|
||||||
let mut display_diver = DisplayDriver::new(DisplayBuffer::new(), native_display);
|
let mut display_diver = DisplayDriver::new(draw_buffer, native_display)?;
|
||||||
Ok(disp_drv_register(&mut display_diver)?)
|
Ok(disp_drv_register(&mut display_diver)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn register_shared<T, C>(shared_native_display: &SharedNativeDisplay<T>) -> Result<Self>
|
pub fn register_shared<T, C>(
|
||||||
|
draw_buffer: &'static DrawBuffer,
|
||||||
|
shared_native_display: &SharedNativeDisplay<T>,
|
||||||
|
) -> Result<Self>
|
||||||
where
|
where
|
||||||
T: DrawTarget<C>,
|
T: DrawTarget<C>,
|
||||||
C: PixelColor + From<Color>,
|
C: PixelColor + From<Color>,
|
||||||
{
|
{
|
||||||
let mut display_diver =
|
let mut display_diver =
|
||||||
DisplayDriver::new_shared(DisplayBuffer::new(), Arc::clone(shared_native_display));
|
DisplayDriver::new_shared(draw_buffer, Arc::clone(shared_native_display))?;
|
||||||
Ok(disp_drv_register(&mut display_diver)?)
|
Ok(disp_drv_register(&mut display_diver)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ impl Display {
|
||||||
|
|
||||||
impl Default for Display {
|
impl Default for Display {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
disp_get_default().expect("LVGL must be initialized")
|
disp_get_default().expect("LVGL must be INITIALIZED")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,21 +86,40 @@ impl DefaultDisplay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DisplayBuffer {}
|
pub struct DrawBuffer {
|
||||||
|
initialized: AtomicBool,
|
||||||
|
refresh_buffer: Mutex<RefCell<heapless::Vec<lvgl_sys::lv_color_t, BUF_SIZE>>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl DisplayBuffer {
|
impl DrawBuffer {
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
unsafe {
|
Self {
|
||||||
lvgl_sys::lv_disp_buf_init(
|
initialized: AtomicBool::new(false),
|
||||||
DRAW_BUFFER.as_mut_ptr(),
|
refresh_buffer: const_mutex(RefCell::new(heapless::Vec::new())),
|
||||||
&mut REFRESH_BUFFER1 as *mut _ as *mut cty::c_void,
|
}
|
||||||
// Box::into_raw(refresh_buffer2) as *mut cty::c_void,
|
}
|
||||||
ptr::null_mut(),
|
|
||||||
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {}
|
fn get_ptr(&self) -> Option<Box<lvgl_sys::lv_disp_buf_t>> {
|
||||||
|
if self
|
||||||
|
.initialized
|
||||||
|
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
let mut inner: MaybeUninit<lvgl_sys::lv_disp_buf_t> = MaybeUninit::uninit();
|
||||||
|
let refresh_buffer_guard = self.refresh_buffer.lock();
|
||||||
|
let draw_buf = unsafe {
|
||||||
|
lvgl_sys::lv_disp_buf_init(
|
||||||
|
inner.as_mut_ptr(),
|
||||||
|
refresh_buffer_guard.borrow_mut().as_mut_ptr() as *mut _ as *mut cty::c_void,
|
||||||
|
ptr::null_mut(),
|
||||||
|
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
||||||
|
);
|
||||||
|
inner.assume_init()
|
||||||
|
};
|
||||||
|
Some(Box::new(draw_buf))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,51 +138,54 @@ where
|
||||||
T: DrawTarget<C>,
|
T: DrawTarget<C>,
|
||||||
C: PixelColor + From<Color>,
|
C: PixelColor + From<Color>,
|
||||||
{
|
{
|
||||||
pub fn new(_display_buffer: DisplayBuffer, native_display: T) -> Self {
|
pub fn new(draw_buffer: &'static DrawBuffer, native_display: T) -> Result<Self> {
|
||||||
let mut disp_drv = unsafe {
|
let mut disp_drv = unsafe {
|
||||||
let mut inner = MaybeUninit::uninit();
|
let mut inner = MaybeUninit::uninit();
|
||||||
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
||||||
inner.assume_init()
|
inner.assume_init()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safety: The variable `disp_buf` is statically allocated, no need to worry about this being dropped.
|
// Safety: The variable `draw_buffer` is statically allocated, no need to worry about this being dropped.
|
||||||
unsafe {
|
disp_drv.buffer = draw_buffer
|
||||||
disp_drv.buffer = DRAW_BUFFER.as_mut_ptr();
|
.get_ptr()
|
||||||
}
|
.map(|ptr| Box::into_raw(ptr) as *mut _)
|
||||||
|
.ok_or(DisplayError::FailedToRegister)?;
|
||||||
|
|
||||||
let mut native_display = ManuallyDrop::new(DisplayUserData {
|
let native_display = DisplayUserData {
|
||||||
display: native_display,
|
display: native_display,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
});
|
};
|
||||||
disp_drv.user_data = &mut native_display as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
disp_drv.user_data =
|
||||||
|
Box::into_raw(Box::new(native_display)) as *mut _ as lvgl_sys::lv_disp_drv_user_data_t;
|
||||||
|
|
||||||
// Sets trampoline pointer to the function implementation using the types (T, C) that
|
// Sets trampoline pointer to the function implementation using the types (T, C) that
|
||||||
// are used in this instance of `DisplayDriver`.
|
// are used in this instance of `DisplayDriver`.
|
||||||
disp_drv.flush_cb = Some(disp_flush_trampoline::<T, C>);
|
disp_drv.flush_cb = Some(disp_flush_trampoline::<T, C>);
|
||||||
|
|
||||||
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
||||||
Self {
|
Ok(Self {
|
||||||
disp_drv,
|
disp_drv,
|
||||||
phantom_color: PhantomData,
|
phantom_color: PhantomData,
|
||||||
phantom_display: PhantomData,
|
phantom_display: PhantomData,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn new_shared(
|
pub fn new_shared(
|
||||||
_display_buffer: DisplayBuffer,
|
draw_buffer: &'static DrawBuffer,
|
||||||
shared_native_display: SharedNativeDisplay<T>,
|
shared_native_display: SharedNativeDisplay<T>,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
let mut disp_drv = unsafe {
|
let mut disp_drv = unsafe {
|
||||||
let mut inner = MaybeUninit::uninit();
|
let mut inner = MaybeUninit::uninit();
|
||||||
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
lvgl_sys::lv_disp_drv_init(inner.as_mut_ptr());
|
||||||
inner.assume_init()
|
inner.assume_init()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safety: The variable `disp_buf` is statically allocated, no need to worry about this being dropped.
|
// Safety: The variable `draw_buffer` is statically allocated, no need to worry about this being dropped.
|
||||||
unsafe {
|
disp_drv.buffer = draw_buffer
|
||||||
disp_drv.buffer = DRAW_BUFFER.as_mut_ptr();
|
.get_ptr()
|
||||||
}
|
.map(|ptr| Box::into_raw(ptr) as *mut _)
|
||||||
|
.ok_or(DisplayError::FailedToRegister)?;
|
||||||
|
|
||||||
let native_display = SharedDisplayUserData {
|
let native_display = SharedDisplayUserData {
|
||||||
display: shared_native_display,
|
display: shared_native_display,
|
||||||
|
@ -178,11 +199,11 @@ where
|
||||||
disp_drv.flush_cb = Some(shared_disp_flush_trampoline::<T, C>);
|
disp_drv.flush_cb = Some(shared_disp_flush_trampoline::<T, C>);
|
||||||
|
|
||||||
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
// We do not store any memory that can be accidentally deallocated by on the Rust side.
|
||||||
Self {
|
Ok(Self {
|
||||||
disp_drv,
|
disp_drv,
|
||||||
phantom_color: PhantomData,
|
phantom_color: PhantomData,
|
||||||
phantom_display: PhantomData,
|
phantom_display: PhantomData,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,20 +55,27 @@ pub mod widgets;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
pub use functions::*;
|
pub use functions::*;
|
||||||
pub use lv_core::*;
|
pub use lv_core::*;
|
||||||
use parking_lot::Mutex;
|
|
||||||
pub use support::*;
|
pub use support::*;
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
|
|
||||||
lazy_static! {
|
struct RunOnce(AtomicBool);
|
||||||
static ref MUTEX: Mutex<AtomicBool> = Mutex::new(AtomicBool::new(false));
|
|
||||||
|
impl RunOnce {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self(AtomicBool::new(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_and_get(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LVGL_INITIALIZED: RunOnce = RunOnce::new();
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
let initialized = MUTEX.lock();
|
if LVGL_INITIALIZED.swap_and_get() {
|
||||||
if initialized
|
|
||||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
unsafe {
|
unsafe {
|
||||||
lvgl_sys::lv_init();
|
lvgl_sys::lv_init();
|
||||||
}
|
}
|
||||||
|
@ -78,20 +85,19 @@ pub fn init() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display::Display;
|
use crate::display::{Display, DrawBuffer};
|
||||||
use embedded_graphics::mock_display::MockDisplay;
|
use embedded_graphics::mock_display::MockDisplay;
|
||||||
use embedded_graphics::pixelcolor::Rgb565;
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
|
|
||||||
pub(crate) fn initialize_test() {
|
pub(crate) fn initialize_test() {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
static RUN_ONCE: Mutex<Option<u8>> = parking_lot::const_mutex(None);
|
static DRAW_BUFFER: DrawBuffer = DrawBuffer::new();
|
||||||
let mut run_once = RUN_ONCE.lock();
|
static ONCE_INIT: RunOnce = RunOnce::new();
|
||||||
|
|
||||||
if run_once.is_none() {
|
if ONCE_INIT.swap_and_get() {
|
||||||
let embedded_graphics_display: MockDisplay<Rgb565> = Default::default();
|
let embedded_graphics_display: MockDisplay<Rgb565> = Default::default();
|
||||||
let _ = Display::register(embedded_graphics_display).unwrap();
|
let _ = Display::register(&DRAW_BUFFER, embedded_graphics_display).unwrap();
|
||||||
*run_once = Some(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue