Compare commits
106 commits
Author | SHA1 | Date | |
---|---|---|---|
601a87d513 | |||
faa3a19215 | |||
029d230cab | |||
ade4ae5a2d | |||
37950d6e2e | |||
763b78e45d | |||
Rafael Caricio | 0a5b5a0416 | ||
Rafael Caricio | a5c27b58ae | ||
cbbdc7e750 | |||
Rafael Caricio | 56cb470dc8 | ||
Rafael Caricio | 043bb52881 | ||
Rafael Caricio | a5ffa62013 | ||
Rafael Caricio | 348963b80d | ||
3791b211b8 | |||
Rafael Caricio | d699101afe | ||
Rafael Caricio | f60d8d999b | ||
Rafael Caricio | ab8b8ae05f | ||
Rafael Caricio | 9b9ea48920 | ||
Rafael Caricio | 7f5bec7366 | ||
Rafael Caricio | 3f226e94a4 | ||
Rafael Caricio | bcd771df47 | ||
Rafael Caricio | da8bae1f55 | ||
d3a2a9eff3 | |||
65b68e0f7c | |||
Rafael Caricio | 81cb1bee4c | ||
Rafael Caricio | 8fe36f6102 | ||
Rafael Caricio | d442ab99cc | ||
Rafael Caricio | f4618fd635 | ||
Rafael Caricio | 385fc6b764 | ||
Rafael Caricio | 0761e36c76 | ||
Rafael Caricio | ef94e5d5d4 | ||
Rafael Caricio | a1aec89456 | ||
Rafael Caricio | 98ae8e0ced | ||
Rafael Caricio | 52fafecdde | ||
Rafael Caricio | 64527c3f2a | ||
Rafael Caricio | adee5994dc | ||
Rafael Caricio | 60c3fc7ed7 | ||
Rafael Caricio | 6a9bd2d87f | ||
Rafael Caricio | e3fd8d5736 | ||
Rafael Caricio | 3e7e056da6 | ||
Rafael Caricio | e70ba3a752 | ||
Rafael Caricio | 38d7a0e974 | ||
Rafael Caricio | 46562f23cf | ||
Rafael Caricio | 8b2879f93b | ||
Rafael Caricio | 657640d206 | ||
Rafael Caricio | b85226aada | ||
Rafael Caricio | 6ddb606855 | ||
Rafael Caricio | 931dead6ab | ||
Rafael Caricio | 5b48d5faad | ||
Rafael Caricio | 78f0dbcefa | ||
Rafael Caricio | 3c8f892c19 | ||
Rafael Caricio | 655300d3e9 | ||
Rafael Caricio | a6bb2e4012 | ||
Rafael Caricio | b8dfd896c2 | ||
Rafael Caricio | fd65ecbe34 | ||
Rafael Caricio | 2277334491 | ||
Rafael Caricio | 78841f1538 | ||
Rafael Caricio | ad5a3deea4 | ||
Rafael Caricio | ceabc135cc | ||
Rafael Caricio | 74ae3a8b5b | ||
Rafael Caricio | dbb1d4a8d1 | ||
Rafael Caricio | cd3397b254 | ||
Rafael Caricio | 0e256957ba | ||
Rafael Caricio | 67cbca4942 | ||
Rafael Caricio | b0113fc747 | ||
Rafael Caricio | de9b6512a7 | ||
Rafael Caricio | d970deaa9d | ||
Rafael Caricio | f0ec94cbf9 | ||
Rafael Caricio | 6f5b771b7d | ||
Rafael Caricio | 55f206e2cd | ||
Rafael Caricio | 311c8c6cc9 | ||
Rafael Caricio | 119d44ef91 | ||
Rafael Caricio | 335261ed6e | ||
Rafael Caricio | 36bae837f8 | ||
80d976ac1f | |||
546781f1f6 | |||
Rafael Caricio | 25efb6da91 | ||
Rafael Caricio | 88d7d426ca | ||
Rafael Caricio | 469fdf2508 | ||
Rafael Caricio | 5015711141 | ||
Rafael Caricio | 66d98430d8 | ||
Rafael Caricio | 3788b4c66b | ||
Rafael Caricio | 461da8814c | ||
Rafael Caricio | e8757f74de | ||
Rafael Caricio | 52e31496c6 | ||
Rafael Caricio | c494d8fac0 | ||
Rafael Caricio | 93a7fd4316 | ||
Rafael Caricio | 59c567ae1c | ||
Rafael Caricio | 460a3d1d30 | ||
Rafael Caricio | 63e33d052f | ||
Rafael Caricio | 11695e20c1 | ||
Rafael Caricio | 9ec78a70cb | ||
Rafael Caricio | 532cf3cf1b | ||
Rafael Caricio | 3faec0e2b1 | ||
fcdf803ab7 | |||
Rafael Caricio | b8a559a6cf | ||
Rafael Caricio | 096d175fc0 | ||
d22c419316 | |||
Rafael Caricio | 0349f1c77a | ||
Rafael Caricio | 2dd59d212d | ||
Rafael Caricio | a72622e1c8 | ||
Rafael Caricio | b132edc6fc | ||
Rafael Caricio | cb8a78137d | ||
Rafael Caricio | e0e3924789 | ||
2fae95bdfb | |||
Rafael Caricio | 81d924e854 |
33
.github/workflows/rust.yml
vendored
Normal file
33
.github/workflows/rust.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
DEP_LV_CONFIG_PATH: /home/runner/work/lvgl-rs/lvgl-rs/examples/include
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Update APT
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Install SDL2
|
||||
run: sudo apt install libsdl2-dev libsdl2-2.0-0
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,4 +12,3 @@ Cargo.lock
|
|||
examples/demo/target/
|
||||
lvgl-sys/target/
|
||||
lvgl/target/
|
||||
lvgl-sys/src/bindings.rs
|
||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "lvgl-sys/vendor/lvgl"]
|
||||
path = lvgl-sys/vendor/lvgl
|
||||
url = https://github.com/littlevgl/lvgl.git
|
||||
url = https://github.com/lvgl/lvgl.git
|
||||
|
|
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at lvgl-rs.coc@caric.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
|
@ -1,6 +1,6 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"lvgl",
|
||||
"lvgl-codegen",
|
||||
"lvgl-sys",
|
||||
"examples/demo",
|
||||
]
|
||||
|
|
88
README.md
88
README.md
|
@ -1,24 +1,41 @@
|
|||
<h1 align="center"> LittlevGL - Open-source Embedded GUI Library in Rust</h1>
|
||||
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
|
||||
|
||||
![Original LittlevGL demo image](lv_demo.png)
|
||||
<h1 align="center"> LVGL - Open-source Embedded GUI Library in Rust</h1>
|
||||
|
||||
![Original LVGL demo image](lv_demo.png)
|
||||
|
||||
<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 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>
|
||||
|
||||
<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://lvgl.io/demos">Live demo</a>
|
||||
<a href="https://lvgl.io/demos">Official live demos</a>
|
||||
</h4>
|
||||
|
||||
---
|
||||
|
||||
![Rust bindings usage demo code.](demo.png)
|
||||
|
||||
## System Build Dependencies
|
||||
|
||||
In order to build the `lvgl` project you will need the following system dependencies to be installed:
|
||||
|
||||
```
|
||||
$ sudo apt install build-essential llvm clang
|
||||
```
|
||||
|
||||
If you want to build the examples, then you will need to install SDL2 as well.
|
||||
|
||||
```
|
||||
$ sudo apt install libsdl2-dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Edit your `Cargo.toml` file dependencies with:
|
||||
|
@ -28,7 +45,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
|
||||
|
@ -44,14 +61,57 @@ 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
|
||||
|
||||
#### Requirements / Limitations
|
||||
|
||||
LittlevGL C libary do allocate memory dynamically and we need to allocate memory on the heap in the Rust side as well
|
||||
([`Box`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html)).
|
||||
That is required, so we can safely provide Rust pointers through FFI. For that reason, we do require
|
||||
[`alloc`](https://doc.rust-lang.org/alloc/) module to be available.
|
||||
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
|
||||
|
||||
[This project contains an example that can run in a desktop simulator.](./examples/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:
|
||||
|
||||
```shell
|
||||
$ brew install sdl2
|
||||
```
|
||||
|
||||
[This project contains examples that can run in a desktop simulator.](./examples)
|
||||
|
||||
First, make sure to pull `lvgl-rs` submodules:
|
||||
```shell
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
```
|
||||
|
||||
Then run the `demo` example:
|
||||
|
||||
```shell
|
||||
$ DEP_LV_CONFIG_PATH=`pwd`/examples/include cargo run --example demo --features="alloc"
|
||||
```
|
||||
|
||||
## Feature Support
|
||||
|
||||
The bindings are still in development. There are many features of LVGL that needs to be exposed by `lvgl-rs`. In
|
||||
this section you can check what is implemented at the moment.
|
||||
|
||||
### Features
|
||||
|
||||
List of LVGL features that impacts the library usage in general.
|
||||
- [x] Displays: We use [`embedded_graphics`](https://docs.rs/embedded-graphics/0.6.2/embedded_graphics/) library to
|
||||
draw to the display. You can use `lvgl-rs` with any of the
|
||||
[`embedded_graphics` supported displays](https://docs.rs/embedded-graphics/0.6.2/embedded_graphics/#supported-displays).
|
||||
- [x] Events: You can listen and trigger events in widget objects.
|
||||
- [x] Styles: You can set styles in any exposed object. We are still missing the possibility of defining global base styles.
|
||||
- [ ] Input Devices
|
||||
- [ ] Fonts
|
||||
- [ ] Images
|
||||
- [ ] File system
|
||||
- [ ] Animations
|
||||
- [ ] Tasks
|
||||
|
||||
### Widgets
|
||||
|
||||
Widgets currently implemented might have some missing features. If the widget you want to use is not exposed or
|
||||
is missing a feature you want to make use, please send a Pull Request or open an issue.
|
||||
|
|
8
examples/README.md
Normal file
8
examples/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
## Examples of how to use various `lvgl-rs` widgets/components
|
||||
|
||||
All examples can be executed using:
|
||||
```shell
|
||||
cargo run --example <name>
|
||||
```
|
||||
|
||||
The `DEP_LV_CONFIG_PATH` environment variable is necessary, as explained in the [README](../README.md).
|
106
examples/arc.rs
Normal file
106
examples/arc.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use cstr_core::CString;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl::style::Style;
|
||||
use lvgl::widgets::{Arc, Label, LabelAlign};
|
||||
use lvgl::{self, Align, Color, Part, State, UI};
|
||||
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<Rgb565> = 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 mut window = Window::new("Arc Example", &output_settings);
|
||||
|
||||
let mut ui = UI::init()?;
|
||||
|
||||
// Implement and register your display:
|
||||
ui.disp_drv_register(display)?;
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_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)?;
|
||||
|
||||
// Create the arc object
|
||||
let mut arc = Arc::new(&mut screen)?;
|
||||
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)?;
|
||||
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)?;
|
||||
|
||||
let mut angle = 0;
|
||||
let mut forward = true;
|
||||
let mut i = 0;
|
||||
|
||||
let mut loop_started = Instant::now();
|
||||
'running: loop {
|
||||
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)?;
|
||||
i += 1;
|
||||
|
||||
ui.task_handler();
|
||||
window.update(ui.get_display_ref().unwrap());
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ui.tick_inc(loop_started.elapsed());
|
||||
loop_started = Instant::now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
83
examples/bar.rs
Normal file
83
examples/bar.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use cstr_core::CString;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl::style::Style;
|
||||
use lvgl::widgets::{Bar, Label, LabelAlign};
|
||||
use lvgl::{self, Align, Animation, Color, Event, LvError, Part, State, Widget, UI};
|
||||
use lvgl_sys;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() -> Result<(), LvError> {
|
||||
let display: SimulatorDisplay<Rgb565> = 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 mut window = Window::new("Bar Example", &output_settings);
|
||||
|
||||
let mut ui = UI::init()?;
|
||||
|
||||
// Implement and register your display:
|
||||
ui.disp_drv_register(display).unwrap();
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_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)?;
|
||||
|
||||
// Create the bar object
|
||||
let mut bar = Bar::new(&mut screen)?;
|
||||
bar.set_size(175, 20)?;
|
||||
bar.set_align(&mut screen, Align::Center, 0, 10)?;
|
||||
bar.set_range(0, 100)?;
|
||||
bar.on_event(|_b, _e| {
|
||||
println!("Completed!");
|
||||
})?;
|
||||
|
||||
// // Set the indicator style for the bar object
|
||||
let mut ind_style = Style::default();
|
||||
ind_style.set_bg_color(State::DEFAULT, Color::from_rgb((100, 245, 100)));
|
||||
bar.add_style(Part::All, ind_style)?;
|
||||
|
||||
let mut loading_lbl = Label::new(&mut screen)?;
|
||||
loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?;
|
||||
loading_lbl.set_align(&mut bar, 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)?;
|
||||
|
||||
let mut i = 0;
|
||||
let mut loop_started = Instant::now();
|
||||
'running: loop {
|
||||
if i > 100 {
|
||||
i = 0;
|
||||
ui.event_send(&mut bar, Event::Clicked)?;
|
||||
}
|
||||
bar.set_value(i, Animation::ON)?;
|
||||
i += 1;
|
||||
|
||||
ui.task_handler();
|
||||
window.update(ui.get_display_ref().unwrap());
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ui.tick_inc(loop_started.elapsed());
|
||||
loop_started = Instant::now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
82
examples/button_click.rs
Normal file
82
examples/button_click.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use cstr_core::CString;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl::style::Style;
|
||||
use lvgl::widgets::{Btn, Label};
|
||||
use lvgl::{self, Align, Color, Event, LvError, Part, State, Widget, UI};
|
||||
use lvgl_sys;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() -> Result<(), LvError> {
|
||||
let display: SimulatorDisplay<Rgb565> = 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 mut window = Window::new("Bar Example", &output_settings);
|
||||
|
||||
let mut ui = UI::init()?;
|
||||
|
||||
// Implement and register your display:
|
||||
ui.disp_drv_register(display)?;
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_act()?;
|
||||
|
||||
let mut screen_style = Style::default();
|
||||
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||
screen.add_style(Part::Main, screen_style)?;
|
||||
|
||||
// Create the button
|
||||
let mut button = Btn::new(&mut screen)?;
|
||||
button.set_align(&mut screen, Align::InLeftMid, 30, 0)?;
|
||||
button.set_size(180, 80)?;
|
||||
let mut btn_lbl = Label::new(&mut button)?;
|
||||
btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str())?;
|
||||
|
||||
let mut btn_state = false;
|
||||
button.on_event(|mut btn, event| {
|
||||
if let lvgl::Event::Clicked = event {
|
||||
if btn_state {
|
||||
let nt = CString::new("Click me!").unwrap();
|
||||
btn_lbl.set_text(nt.as_c_str()).unwrap();
|
||||
} else {
|
||||
let nt = CString::new("Clicked!").unwrap();
|
||||
btn_lbl.set_text(nt.as_c_str()).unwrap();
|
||||
}
|
||||
btn_state = !btn_state;
|
||||
println!("Clicked! Inner..");
|
||||
btn.toggle().unwrap();
|
||||
}
|
||||
})?;
|
||||
|
||||
let mut loop_started = Instant::now();
|
||||
'running: loop {
|
||||
ui.task_handler();
|
||||
window.update(ui.get_display_ref().unwrap());
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::MouseButtonUp {
|
||||
mouse_btn: _,
|
||||
point,
|
||||
} => {
|
||||
println!("Clicked on: {:?}", point);
|
||||
// Send a event to the button directly
|
||||
ui.event_send(&mut button, Event::Clicked)?;
|
||||
}
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ui.tick_inc(loop_started.elapsed());
|
||||
loop_started = Instant::now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
101
examples/demo.rs
Normal file
101
examples/demo.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use cstr_core::CString;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl;
|
||||
use lvgl::style::Style;
|
||||
use lvgl::widgets::{Label, LabelAlign};
|
||||
use lvgl::{Align, Color, LvError, Part, State, Widget, UI};
|
||||
use lvgl_sys;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
fn main() -> Result<(), LvError> {
|
||||
let display: SimulatorDisplay<Rgb565> = 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 mut window = Window::new("PineTime", &output_settings);
|
||||
|
||||
let mut ui = UI::init()?;
|
||||
|
||||
// Implement and register your display:
|
||||
ui.disp_drv_register(display).unwrap();
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_act()?;
|
||||
|
||||
let mut screen_style = Style::default();
|
||||
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||
screen_style.set_radius(State::DEFAULT, 0);
|
||||
screen.add_style(Part::Main, screen_style)?;
|
||||
|
||||
let mut time = Label::new(&mut screen)?;
|
||||
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, 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(&mut screen)?;
|
||||
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(&mut screen)?;
|
||||
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)?;
|
||||
|
||||
let mut i = 0;
|
||||
let mut loop_started = Instant::now();
|
||||
'running: loop {
|
||||
if i > 59 {
|
||||
i = 0;
|
||||
}
|
||||
let val = CString::new(format!("21:{:02}", i)).unwrap();
|
||||
time.set_text(&val)?;
|
||||
i = 1 + i;
|
||||
|
||||
ui.task_handler();
|
||||
window.update(ui.get_display_ref().unwrap());
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
ui.tick_inc(loop_started.elapsed());
|
||||
loop_started = Instant::now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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:
|
||||
//
|
||||
// font_declare! {
|
||||
// NotoSansNumeric80 = noto_sans_numeric_80;
|
||||
// };
|
||||
//
|
||||
extern "C" {
|
||||
pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "demo"
|
||||
version = "0.1.0"
|
||||
authors = ["Rafael Caricio <crates@caric.io>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lvgl = { path = "../../lvgl" }
|
||||
lvgl-sys = { path = "../../lvgl-sys" }
|
||||
embedded-graphics = "0.6"
|
||||
embedded-graphics-simulator = "0.2.0"
|
|
@ -1,12 +0,0 @@
|
|||
From base of the repository, install LVGL git submodule.
|
||||
|
||||
```
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Run this demo:
|
||||
|
||||
```
|
||||
cd examples/demo/
|
||||
DEP_LV_CONFIG_PATH=./include cargo run
|
||||
```
|
|
@ -1,123 +0,0 @@
|
|||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl;
|
||||
use lvgl::{Object, UI};
|
||||
use lvgl_sys;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut display: SimulatorDisplay<Rgb565> = 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 mut window = Window::new("PineTime", &output_settings);
|
||||
|
||||
let mut ui = UI::init().unwrap();
|
||||
|
||||
// Implement and register your display:
|
||||
let display_driver = lvgl::DisplayDriver::new(&mut display);
|
||||
ui.disp_drv_register(display_driver);
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_act();
|
||||
|
||||
let font_roboto_28 = unsafe { &lvgl_sys::lv_font_roboto_28 };
|
||||
let font_noto_sans_numeric_28 = unsafe { ¬o_sans_numeric_80 };
|
||||
|
||||
let mut screen_style = lvgl::Style::new();
|
||||
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_radius(0);
|
||||
screen.set_style(screen_style);
|
||||
|
||||
let mut time = lvgl::Label::new(&mut screen);
|
||||
let mut style_time = lvgl::Style::new();
|
||||
style_time.set_text_font(font_noto_sans_numeric_28);
|
||||
style_time.set_text_color(lvgl::Color::from_rgb((255, 255, 255)));
|
||||
time.set_style(style_time);
|
||||
time.set_align(&mut screen, lvgl::Align::InLeftMid, 20, 0);
|
||||
time.set_text("20:46");
|
||||
time.set_width(240);
|
||||
time.set_height(240);
|
||||
|
||||
let mut bt = lvgl::Label::new(&mut screen);
|
||||
let mut style_bt = lvgl::Style::new();
|
||||
style_bt.set_text_font(font_roboto_28);
|
||||
let style_power = style_bt.clone();
|
||||
bt.set_style(style_bt);
|
||||
bt.set_width(50);
|
||||
bt.set_height(80);
|
||||
bt.set_recolor(true);
|
||||
bt.set_text("#5794f2 \u{F293}#");
|
||||
bt.set_label_align(lvgl::LabelAlign::Left);
|
||||
bt.set_align(&mut screen, lvgl::Align::InTopLeft, 0, 0);
|
||||
|
||||
let mut power = lvgl::Label::new(&mut screen);
|
||||
power.set_style(style_power);
|
||||
power.set_recolor(true);
|
||||
power.set_width(80);
|
||||
power.set_height(20);
|
||||
power.set_text("#fade2a 20%#");
|
||||
power.set_label_align(lvgl::LabelAlign::Right);
|
||||
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 closure_ui = threaded_ui.clone();
|
||||
let tick_thr = std::thread::spawn(move || loop {
|
||||
let period = Duration::from_millis(250);
|
||||
closure_ui.lock().unwrap().tick_inc(period);
|
||||
|
||||
sleep(period);
|
||||
if read_ch.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
let mut i = 0;
|
||||
'running: loop {
|
||||
if i > 59 {
|
||||
i = 0;
|
||||
}
|
||||
time.set_text(format!("21:{:02}", i).as_str());
|
||||
i = 1 + i;
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
threaded_ui.lock().unwrap().task_handler();
|
||||
|
||||
window.update(&display);
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop_ch.send(true).unwrap();
|
||||
tick_thr.join().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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_declare! {
|
||||
// NotoSansNumeric80 = noto_sans_numeric_80;
|
||||
// };
|
||||
//
|
||||
extern "C" {
|
||||
pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t;
|
||||
}
|
89
examples/gauge.rs
Normal file
89
examples/gauge.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics_simulator::{
|
||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||
};
|
||||
use lvgl::style::{Opacity, Style};
|
||||
use lvgl::widgets::Gauge;
|
||||
use lvgl::{self, Align, Color, LvError, Part, State, Widget, UI};
|
||||
use lvgl_sys;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() -> Result<(), LvError> {
|
||||
let display: SimulatorDisplay<Rgb565> = 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 mut window = Window::new("Gauge Example", &output_settings);
|
||||
|
||||
let mut ui = UI::init()?;
|
||||
|
||||
// Implement and register your display:
|
||||
ui.disp_drv_register(display)?;
|
||||
|
||||
// Create screen and widgets
|
||||
let mut screen = ui.scr_act()?;
|
||||
|
||||
let mut screen_style = Style::default();
|
||||
screen_style.set_bg_color(State::DEFAULT, Color::from_rgb((0, 0, 0)));
|
||||
screen.add_style(Part::Main, screen_style)?;
|
||||
|
||||
// Create the gauge
|
||||
let mut gauge_style = Style::default();
|
||||
// Set a background color and a radius
|
||||
gauge_style.set_radius(State::DEFAULT, 5);
|
||||
gauge_style.set_bg_opa(State::DEFAULT, Opacity::OPA_COVER);
|
||||
gauge_style.set_bg_color(State::DEFAULT, Color::from_rgb((192, 192, 192)));
|
||||
// Set some paddings
|
||||
gauge_style.set_pad_inner(State::DEFAULT, 20);
|
||||
gauge_style.set_pad_top(State::DEFAULT, 20);
|
||||
gauge_style.set_pad_left(State::DEFAULT, 5);
|
||||
gauge_style.set_pad_right(State::DEFAULT, 5);
|
||||
|
||||
gauge_style.set_scale_end_color(State::DEFAULT, Color::from_rgb((255, 0, 0)));
|
||||
gauge_style.set_line_color(State::DEFAULT, Color::from_rgb((255, 255, 255)));
|
||||
gauge_style.set_scale_grad_color(State::DEFAULT, Color::from_rgb((0, 0, 255)));
|
||||
gauge_style.set_line_width(State::DEFAULT, 2);
|
||||
gauge_style.set_scale_end_line_width(State::DEFAULT, 4);
|
||||
gauge_style.set_scale_end_border_width(State::DEFAULT, 4);
|
||||
|
||||
let mut gauge = Gauge::new(&mut screen)?;
|
||||
gauge.add_style(Part::Main, gauge_style)?;
|
||||
gauge.set_align(&mut screen, Align::Center, 0, 0)?;
|
||||
gauge.set_value(0, 50)?;
|
||||
|
||||
let mut i = 0;
|
||||
let mut loop_started = Instant::now();
|
||||
'running: loop {
|
||||
gauge.set_value(0, i)?;
|
||||
|
||||
ui.task_handler();
|
||||
window.update(ui.get_display_ref().unwrap());
|
||||
|
||||
for event in window.events() {
|
||||
match event {
|
||||
SimulatorEvent::MouseButtonUp {
|
||||
mouse_btn: _,
|
||||
point,
|
||||
} => {
|
||||
println!("Clicked on: {:?}", point);
|
||||
}
|
||||
SimulatorEvent::Quit => break 'running,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if i > 99 {
|
||||
i = 0;
|
||||
} else {
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
ui.tick_inc(loop_started.elapsed());
|
||||
loop_started = Instant::now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
701
examples/include/lv_conf.h
Normal file
701
examples/include/lv_conf.h
Normal file
|
@ -0,0 +1,701 @@
|
|||
/**
|
||||
* @file lv_conf.h
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER
|
||||
*/
|
||||
|
||||
#if 1 /*Set it to "1" to enable content*/
|
||||
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_H
|
||||
/* clang-format off */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*====================
|
||||
Graphical settings
|
||||
*====================*/
|
||||
|
||||
/* Maximal horizontal and vertical resolution to support by the library.*/
|
||||
#define LV_HOR_RES_MAX (240)
|
||||
#define LV_VER_RES_MAX (240)
|
||||
|
||||
/* Color depth:
|
||||
* - 1: 1 byte per pixel
|
||||
* - 8: RGB332
|
||||
* - 16: RGB565
|
||||
* - 32: ARGB8888
|
||||
*/
|
||||
#define LV_COLOR_DEPTH 16
|
||||
|
||||
/* Swap the 2 bytes of RGB565 color.
|
||||
* Useful if the display has a 8 bit interface (e.g. SPI)*/
|
||||
#define LV_COLOR_16_SWAP 0
|
||||
|
||||
/* 1: Enable screen transparency.
|
||||
* Useful for OSD or other overlapping GUIs.
|
||||
* Requires `LV_COLOR_DEPTH = 32` colors and the screen's style should be modified: `style.body.opa = ...`*/
|
||||
#define LV_COLOR_SCREEN_TRANSP 0
|
||||
|
||||
/*Images pixels with this color will not be drawn (with chroma keying)*/
|
||||
#define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/
|
||||
|
||||
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
|
||||
#define LV_ANTIALIAS 1
|
||||
|
||||
/* Default display refresh period.
|
||||
* Can be changed in the display driver (`lv_disp_drv_t`).*/
|
||||
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
|
||||
|
||||
/* Dot Per Inch: used to initialize default sizes.
|
||||
* E.g. a button with width = LV_DPI / 2 -> half inch wide
|
||||
* (Not so important, you can adjust it to modify default sizes and spaces)*/
|
||||
#define LV_DPI 130 /*[px]*/
|
||||
|
||||
/* The the real width of the display changes some default values:
|
||||
* default object sizes, layout of examples, etc.
|
||||
* According to the width of the display (hor. res. / dpi)
|
||||
* the displays fall in 4 categories.
|
||||
* The 4th is extra large which has no upper limit so not listed here
|
||||
* The upper limit of the categories are set below in 0.1 inch unit.
|
||||
*/
|
||||
#define LV_DISP_SMALL_LIMIT 30
|
||||
#define LV_DISP_MEDIUM_LIMIT 50
|
||||
#define LV_DISP_LARGE_LIMIT 70
|
||||
|
||||
/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */
|
||||
typedef int16_t lv_coord_t;
|
||||
|
||||
/*=========================
|
||||
Memory manager settings
|
||||
*=========================*/
|
||||
|
||||
/* LittelvGL's internal memory manager's settings.
|
||||
* The graphical objects and other related data are stored here. */
|
||||
|
||||
/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */
|
||||
#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 (1048576U) // 1Mb
|
||||
|
||||
/* Complier prefix for a big array declaration */
|
||||
# define LV_MEM_ATTR
|
||||
|
||||
/* Set an address for the memory pool instead of allocating it as an array.
|
||||
* Can be in external SRAM too. */
|
||||
# define LV_MEM_ADR 0
|
||||
|
||||
/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */
|
||||
# define LV_MEM_AUTO_DEFRAG 1
|
||||
#else /*LV_MEM_CUSTOM*/
|
||||
# define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
|
||||
# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
|
||||
# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
|
||||
#endif /*LV_MEM_CUSTOM*/
|
||||
|
||||
/* Garbage Collector settings
|
||||
* Used if lvgl is binded to higher level language and the memory is managed by that language */
|
||||
#define LV_ENABLE_GC 0
|
||||
#if LV_ENABLE_GC != 0
|
||||
# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
|
||||
# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
|
||||
# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
|
||||
#endif /* LV_ENABLE_GC */
|
||||
|
||||
/*=======================
|
||||
Input device settings
|
||||
*=======================*/
|
||||
|
||||
/* Input device default settings.
|
||||
* Can be changed in the Input device driver (`lv_indev_drv_t`)*/
|
||||
|
||||
/* Input device read period in milliseconds */
|
||||
#define LV_INDEV_DEF_READ_PERIOD 30
|
||||
|
||||
/* Drag threshold in pixels */
|
||||
#define LV_INDEV_DEF_DRAG_LIMIT 10
|
||||
|
||||
/* Drag throw slow-down in [%]. Greater value -> faster slow-down */
|
||||
#define LV_INDEV_DEF_DRAG_THROW 10
|
||||
|
||||
/* Long press time in milliseconds.
|
||||
* Time to send `LV_EVENT_LONG_PRESSSED`) */
|
||||
#define LV_INDEV_DEF_LONG_PRESS_TIME 400
|
||||
|
||||
/* Repeated trigger period in long press [ms]
|
||||
* Time between `LV_EVENT_LONG_PRESSED_REPEAT */
|
||||
#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
|
||||
|
||||
|
||||
/* Gesture threshold in pixels */
|
||||
#define LV_INDEV_DEF_GESTURE_LIMIT 50
|
||||
|
||||
/* Gesture min velocity at release before swipe (pixels)*/
|
||||
#define LV_INDEV_DEF_GESTURE_MIN_VELOCITY 3
|
||||
|
||||
/*==================
|
||||
* Feature usage
|
||||
*==================*/
|
||||
|
||||
/*1: Enable the Animations */
|
||||
#define LV_USE_ANIMATION 1
|
||||
#if LV_USE_ANIMATION
|
||||
|
||||
/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_anim_user_data_t;
|
||||
|
||||
#endif
|
||||
|
||||
/* 1: Enable shadow drawing*/
|
||||
#define LV_USE_SHADOW 1
|
||||
#if LV_USE_SHADOW
|
||||
/* Allow buffering some shadow calculation
|
||||
* LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer,
|
||||
* where shadow size is `shadow_width + radius`
|
||||
* Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
|
||||
#define LV_SHADOW_CACHE_SIZE 0
|
||||
#endif
|
||||
|
||||
/* 1: Use other blend modes than normal (`LV_BLEND_MODE_...`)*/
|
||||
#define LV_USE_BLEND_MODES 1
|
||||
|
||||
/* 1: Use the `opa_scale` style property to set the opacity of an object and its children at once*/
|
||||
#define LV_USE_OPA_SCALE 1
|
||||
|
||||
/* 1: Use image zoom and rotation*/
|
||||
#define LV_USE_IMG_TRANSFORM 1
|
||||
|
||||
/* 1: Enable object groups (for keyboard/encoder navigation) */
|
||||
#define LV_USE_GROUP 1
|
||||
#if LV_USE_GROUP
|
||||
typedef void * lv_group_user_data_t;
|
||||
#endif /*LV_USE_GROUP*/
|
||||
|
||||
/* 1: Enable GPU interface*/
|
||||
#define LV_USE_GPU 1 /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
|
||||
#define LV_USE_GPU_STM32_DMA2D 0
|
||||
|
||||
/* 1: Enable file system (might be required for images */
|
||||
#define LV_USE_FILESYSTEM 1
|
||||
#if LV_USE_FILESYSTEM
|
||||
/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_fs_drv_user_data_t;
|
||||
#endif
|
||||
|
||||
/*1: Add a `user_data` to drivers and objects*/
|
||||
#define LV_USE_USER_DATA 1
|
||||
|
||||
/*1: Show CPU usage and FPS count in the right bottom corner*/
|
||||
#define LV_USE_PERF_MONITOR 0
|
||||
|
||||
/*1: Use the functions and types from the older API if possible */
|
||||
#define LV_USE_API_EXTENSION_V6 1
|
||||
|
||||
/*========================
|
||||
* Image decoder and cache
|
||||
*========================*/
|
||||
|
||||
/* 1: Enable indexed (palette) images */
|
||||
#define LV_IMG_CF_INDEXED 1
|
||||
|
||||
/* 1: Enable alpha indexed images */
|
||||
#define LV_IMG_CF_ALPHA 1
|
||||
|
||||
/* Default image cache size. Image caching keeps the images opened.
|
||||
* If only the built-in image formats are used there is no real advantage of caching.
|
||||
* (I.e. no new image decoder is added)
|
||||
* With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
|
||||
* However the opened images might consume additional RAM.
|
||||
* LV_IMG_CACHE_DEF_SIZE must be >= 1 */
|
||||
#define LV_IMG_CACHE_DEF_SIZE 1
|
||||
|
||||
/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_img_decoder_user_data_t;
|
||||
|
||||
/*=====================
|
||||
* Compiler settings
|
||||
*====================*/
|
||||
/* Define a custom attribute to `lv_tick_inc` function */
|
||||
#define LV_ATTRIBUTE_TICK_INC
|
||||
|
||||
/* Define a custom attribute to `lv_task_handler` function */
|
||||
#define LV_ATTRIBUTE_TASK_HANDLER
|
||||
|
||||
/* Define a custom attribute to `lv_disp_flush_ready` function */
|
||||
#define LV_ATTRIBUTE_FLUSH_READY
|
||||
|
||||
/* With size optimization (-Os) the compiler might not align data to
|
||||
* 4 or 8 byte boundary. This alignment will be explicitly applied where needed.
|
||||
* E.g. __attribute__((aligned(4))) */
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
|
||||
/* Attribute to mark large constant arrays for example
|
||||
* font's bitmaps */
|
||||
#define LV_ATTRIBUTE_LARGE_CONST
|
||||
|
||||
/* Prefix performance critical functions to place them into a faster memory (e.g RAM)
|
||||
* Uses 15-20 kB extra memory */
|
||||
#define LV_ATTRIBUTE_FAST_MEM
|
||||
|
||||
/* Export integer constant to binding.
|
||||
* This macro is used with constants in the form of LV_<CONST> that
|
||||
* should also appear on lvgl binding API such as Micropython
|
||||
*
|
||||
* The default value just prevents a GCC warning.
|
||||
*/
|
||||
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning
|
||||
|
||||
/*===================
|
||||
* HAL settings
|
||||
*==================*/
|
||||
|
||||
/* 1: use a custom tick source.
|
||||
* It removes the need to manually update the tick with `lv_tick_inc`) */
|
||||
#define LV_TICK_CUSTOM 0
|
||||
#if LV_TICK_CUSTOM == 1
|
||||
#define LV_TICK_CUSTOM_INCLUDE "something.h" /*Header for the sys time function*/
|
||||
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current systime in ms*/
|
||||
#endif /*LV_TICK_CUSTOM*/
|
||||
|
||||
typedef void * lv_disp_drv_user_data_t; /*Type of user data in the display driver*/
|
||||
typedef void * lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/
|
||||
|
||||
/*================
|
||||
* Log settings
|
||||
*===============*/
|
||||
|
||||
/*1: Enable the log module*/
|
||||
#define LV_USE_LOG 0
|
||||
#if LV_USE_LOG
|
||||
/* How important log should be added:
|
||||
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
|
||||
* LV_LOG_LEVEL_INFO Log important events
|
||||
* LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
|
||||
* LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
|
||||
* LV_LOG_LEVEL_NONE Do not log anything
|
||||
*/
|
||||
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
|
||||
|
||||
/* 1: Print the log with 'printf';
|
||||
* 0: user need to register a callback with `lv_log_register_print_cb`*/
|
||||
# define LV_LOG_PRINTF 0
|
||||
#endif /*LV_USE_LOG*/
|
||||
|
||||
/*=================
|
||||
* Debug settings
|
||||
*================*/
|
||||
|
||||
/* If Debug is enabled LittelvGL validates the parameters of the functions.
|
||||
* If an invalid parameter is found an error log message is printed and
|
||||
* the MCU halts at the error. (`LV_USE_LOG` should be enabled)
|
||||
* If you are debugging the MCU you can pause
|
||||
* the debugger to see exactly where the issue is.
|
||||
*
|
||||
* The behavior of asserts can be overwritten by redefining them here.
|
||||
* E.g. #define LV_ASSERT_MEM(p) <my_assert_code>
|
||||
*/
|
||||
#define LV_USE_DEBUG 1
|
||||
#if LV_USE_DEBUG
|
||||
|
||||
/*Check if the parameter is NULL. (Quite fast) */
|
||||
#define LV_USE_ASSERT_NULL 1
|
||||
|
||||
/*Checks is the memory is successfully allocated or no. (Quite fast)*/
|
||||
#define LV_USE_ASSERT_MEM 1
|
||||
|
||||
/*Check the integrity of `lv_mem` after critical operations. (Slow)*/
|
||||
#define LV_USE_ASSERT_MEM_INTEGRITY 0
|
||||
|
||||
/* Check the strings.
|
||||
* Search for NULL, very long strings, invalid characters, and unnatural repetitions. (Slow)
|
||||
* If disabled `LV_USE_ASSERT_NULL` will be performed instead (if it's enabled) */
|
||||
#define LV_USE_ASSERT_STR 0
|
||||
|
||||
/* Check NULL, the object's type and existence (e.g. not deleted). (Quite slow)
|
||||
* If disabled `LV_USE_ASSERT_NULL` will be performed instead (if it's enabled) */
|
||||
#define LV_USE_ASSERT_OBJ 0
|
||||
|
||||
/*Check if the styles are properly initialized. (Fast)*/
|
||||
#define LV_USE_ASSERT_STYLE 0
|
||||
|
||||
#endif /*LV_USE_DEBUG*/
|
||||
|
||||
/*==================
|
||||
* FONT USAGE
|
||||
*===================*/
|
||||
|
||||
/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel.
|
||||
* The symbols are available via `LV_SYMBOL_...` defines
|
||||
* More info about fonts: https://docs.lvgl.com/#Fonts
|
||||
* To create a new font go to: https://lvgl.com/ttf-font-to-c-array
|
||||
*/
|
||||
|
||||
/* Montserrat fonts with bpp = 4
|
||||
* https://fonts.google.com/specimen/Montserrat */
|
||||
#define LV_FONT_MONTSERRAT_12 0
|
||||
#define LV_FONT_MONTSERRAT_14 0
|
||||
#define LV_FONT_MONTSERRAT_16 1
|
||||
#define LV_FONT_MONTSERRAT_18 0
|
||||
#define LV_FONT_MONTSERRAT_20 0
|
||||
#define LV_FONT_MONTSERRAT_22 0
|
||||
#define LV_FONT_MONTSERRAT_24 0
|
||||
#define LV_FONT_MONTSERRAT_26 0
|
||||
#define LV_FONT_MONTSERRAT_28 0
|
||||
#define LV_FONT_MONTSERRAT_30 0
|
||||
#define LV_FONT_MONTSERRAT_32 0
|
||||
#define LV_FONT_MONTSERRAT_34 0
|
||||
#define LV_FONT_MONTSERRAT_36 0
|
||||
#define LV_FONT_MONTSERRAT_38 0
|
||||
#define LV_FONT_MONTSERRAT_40 0
|
||||
#define LV_FONT_MONTSERRAT_42 0
|
||||
#define LV_FONT_MONTSERRAT_44 0
|
||||
#define LV_FONT_MONTSERRAT_46 0
|
||||
#define LV_FONT_MONTSERRAT_48 0
|
||||
|
||||
/* Demonstrate special features */
|
||||
#define LV_FONT_MONTSERRAT_12_SUBPX 0
|
||||
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
|
||||
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, PErisan letters and all their forms*/
|
||||
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
|
||||
|
||||
/*Pixel perfect monospace font
|
||||
* http://pelulamu.net/unscii/ */
|
||||
#define LV_FONT_UNSCII_8 0
|
||||
|
||||
/* Optionally declare your custom fonts here.
|
||||
* You can use these fonts as default font too
|
||||
* and they will be available globally. E.g.
|
||||
* #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
|
||||
* LV_FONT_DECLARE(my_font_2)
|
||||
*/
|
||||
#define LV_FONT_CUSTOM_DECLARE
|
||||
|
||||
/* Enable it if you have fonts with a lot of characters.
|
||||
* The limit depends on the font size, font face and bpp
|
||||
* but with > 10,000 characters if you see issues probably you need to enable it.*/
|
||||
#define LV_FONT_FMT_TXT_LARGE 0
|
||||
|
||||
/* Set the pixel order of the display.
|
||||
* Important only if "subpx fonts" are used.
|
||||
* With "normal" font it doesn't matter.
|
||||
*/
|
||||
#define LV_FONT_SUBPX_BGR 0
|
||||
|
||||
/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_font_user_data_t;
|
||||
|
||||
/*================
|
||||
* THEME USAGE
|
||||
*================*/
|
||||
|
||||
/*Always enable at least on theme*/
|
||||
|
||||
/* No theme, you can apply your styles as you need
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_EMPTY 1
|
||||
|
||||
/*Simple to the create your theme based on it
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_TEMPLATE 1
|
||||
|
||||
/* A fast and impressive theme.
|
||||
* Flags:
|
||||
* LV_THEME_MATERIAL_FLAG_LIGHT: light theme
|
||||
* LV_THEME_MATERIAL_FLAG_DARK: dark theme*/
|
||||
#define LV_USE_THEME_MATERIAL 1
|
||||
|
||||
/* Mono-color theme for monochrome displays.
|
||||
* If LV_THEME_DEFAULT_COLOR_PRIMARY is LV_COLOR_BLACK the
|
||||
* texts and borders will be black and the background will be
|
||||
* white. Else the colors are inverted.
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_MONO 1
|
||||
|
||||
#define LV_THEME_DEFAULT_INCLUDE <stdint.h> /*Include a header for the init. function*/
|
||||
#define LV_THEME_DEFAULT_INIT lv_theme_material_init
|
||||
#define LV_THEME_DEFAULT_COLOR_PRIMARY LV_COLOR_RED
|
||||
#define LV_THEME_DEFAULT_COLOR_SECONDARY LV_COLOR_BLUE
|
||||
#define LV_THEME_DEFAULT_FLAG LV_THEME_MATERIAL_FLAG_LIGHT
|
||||
#define LV_THEME_DEFAULT_FONT_SMALL &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_NORMAL &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_SUBTITLE &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_TITLE &lv_font_montserrat_16
|
||||
|
||||
/*=================
|
||||
* Text settings
|
||||
*=================*/
|
||||
|
||||
/* Select a character encoding for strings.
|
||||
* Your IDE or editor should have the same character encoding
|
||||
* - LV_TXT_ENC_UTF8
|
||||
* - LV_TXT_ENC_ASCII
|
||||
* */
|
||||
#define LV_TXT_ENC LV_TXT_ENC_UTF8
|
||||
|
||||
/*Can break (wrap) texts on these chars*/
|
||||
#define LV_TXT_BREAK_CHARS " ,.;:-_"
|
||||
|
||||
/* If a word is at least this long, will break wherever "prettiest"
|
||||
* To disable, set to a value <= 0 */
|
||||
#define LV_TXT_LINE_BREAK_LONG_LEN 0
|
||||
|
||||
/* Minimum number of characters in a long word to put on a line before a break.
|
||||
* Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
|
||||
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
|
||||
|
||||
/* Minimum number of characters in a long word to put on a line after a break.
|
||||
* Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
|
||||
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
|
||||
|
||||
/* The control character to use for signalling text recoloring. */
|
||||
#define LV_TXT_COLOR_CMD "#"
|
||||
|
||||
/* Support bidirectional texts.
|
||||
* Allows mixing Left-to-Right and Right-to-Left texts.
|
||||
* The direction will be processed according to the Unicode Bidirectioanl Algorithm:
|
||||
* https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
|
||||
#define LV_USE_BIDI 0
|
||||
#if LV_USE_BIDI
|
||||
/* Set the default direction. Supported values:
|
||||
* `LV_BIDI_DIR_LTR` Left-to-Right
|
||||
* `LV_BIDI_DIR_RTL` Right-to-Left
|
||||
* `LV_BIDI_DIR_AUTO` detect texts base direction */
|
||||
#define LV_BIDI_BASE_DIR_DEF LV_BIDI_DIR_AUTO
|
||||
#endif
|
||||
|
||||
/* Enable Arabic/Persian processing
|
||||
* In these languages characters should be replaced with
|
||||
* an other form based on their position in the text */
|
||||
#define LV_USE_ARABIC_PERSIAN_CHARS 0
|
||||
|
||||
/*Change the built in (v)snprintf functions*/
|
||||
#define LV_SPRINTF_CUSTOM 0
|
||||
#if LV_SPRINTF_CUSTOM
|
||||
# define LV_SPRINTF_INCLUDE <stdio.h>
|
||||
# define lv_snprintf snprintf
|
||||
# define lv_vsnprintf vsnprintf
|
||||
#else /*!LV_SPRINTF_CUSTOM*/
|
||||
# define LV_SPRINTF_DISABLE_FLOAT 1
|
||||
#endif /*LV_SPRINTF_CUSTOM*/
|
||||
|
||||
/*===================
|
||||
* LV_OBJ SETTINGS
|
||||
*==================*/
|
||||
|
||||
#if LV_USE_USER_DATA
|
||||
/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_obj_user_data_t;
|
||||
/*Provide a function to free user data*/
|
||||
#define LV_USE_USER_DATA_FREE 0
|
||||
#if LV_USE_USER_DATA_FREE
|
||||
# define LV_USER_DATA_FREE_INCLUDE "something.h" /*Header for user data free function*/
|
||||
/* Function prototype : void user_data_free(lv_obj_t * obj); */
|
||||
# define LV_USER_DATA_FREE (user_data_free) /*Invoking for user data free function*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/
|
||||
#define LV_USE_OBJ_REALIGN 1
|
||||
|
||||
/* Enable to make the object clickable on a larger area.
|
||||
* LV_EXT_CLICK_AREA_OFF or 0: Disable this feature
|
||||
* LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px)
|
||||
* LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px)
|
||||
*/
|
||||
#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_TINY
|
||||
|
||||
/*==================
|
||||
* LV OBJ X USAGE
|
||||
*================*/
|
||||
/*
|
||||
* Documentation of the object types: https://docs.lvgl.com/#Object-types
|
||||
*/
|
||||
|
||||
/*Arc (dependencies: -)*/
|
||||
#define LV_USE_ARC 1
|
||||
|
||||
/*Bar (dependencies: -)*/
|
||||
#define LV_USE_BAR 1
|
||||
|
||||
/*Button (dependencies: lv_cont*/
|
||||
#define LV_USE_BTN 1
|
||||
|
||||
/*Button matrix (dependencies: -)*/
|
||||
#define LV_USE_BTNMATRIX 1
|
||||
|
||||
/*Calendar (dependencies: -)*/
|
||||
#define LV_USE_CALENDAR 1
|
||||
|
||||
/*Canvas (dependencies: lv_img)*/
|
||||
#define LV_USE_CANVAS 1
|
||||
|
||||
/*Check box (dependencies: lv_btn, lv_label)*/
|
||||
#define LV_USE_CHECKBOX 1
|
||||
|
||||
/*Chart (dependencies: -)*/
|
||||
#define LV_USE_CHART 1
|
||||
#if LV_USE_CHART
|
||||
# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 256
|
||||
#endif
|
||||
|
||||
/*Container (dependencies: -*/
|
||||
#define LV_USE_CONT 1
|
||||
|
||||
/*Color picker (dependencies: -*/
|
||||
#define LV_USE_CPICKER 1
|
||||
|
||||
/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
|
||||
#define LV_USE_DROPDOWN 1
|
||||
#if LV_USE_DROPDOWN != 0
|
||||
/*Open and close default animation time [ms] (0: no animation)*/
|
||||
# define LV_DROPDOWN_DEF_ANIM_TIME 200
|
||||
#endif
|
||||
|
||||
/*Gauge (dependencies:lv_bar, lv_linemeter)*/
|
||||
#define LV_USE_GAUGE 1
|
||||
|
||||
/*Image (dependencies: lv_label*/
|
||||
#define LV_USE_IMG 1
|
||||
|
||||
/*Image Button (dependencies: lv_btn*/
|
||||
#define LV_USE_IMGBTN 1
|
||||
#if LV_USE_IMGBTN
|
||||
/*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
|
||||
# define LV_IMGBTN_TILED 0
|
||||
#endif
|
||||
|
||||
/*Keyboard (dependencies: lv_btnm)*/
|
||||
#define LV_USE_KEYBOARD 1
|
||||
|
||||
/*Label (dependencies: -*/
|
||||
#define LV_USE_LABEL 1
|
||||
#if LV_USE_LABEL != 0
|
||||
/*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_ROLL/ROLL_CIRC' mode*/
|
||||
# define LV_LABEL_DEF_SCROLL_SPEED 25
|
||||
|
||||
/* Waiting period at beginning/end of animation cycle */
|
||||
# define LV_LABEL_WAIT_CHAR_COUNT 3
|
||||
|
||||
/*Enable selecting text of the label */
|
||||
# define LV_LABEL_TEXT_SEL 0
|
||||
|
||||
/*Store extra some info in labels (12 bytes) to speed up drawing of very long texts*/
|
||||
# define LV_LABEL_LONG_TXT_HINT 0
|
||||
#endif
|
||||
|
||||
/*LED (dependencies: -)*/
|
||||
#define LV_USE_LED 1
|
||||
#if LV_USE_LED
|
||||
# define LV_LED_BRIGHT_MIN 120 /*Minimal brightness*/
|
||||
# define LV_LED_BRIGHT_MAX 255 /*Maximal brightness*/
|
||||
#endif
|
||||
|
||||
/*Line (dependencies: -*/
|
||||
#define LV_USE_LINE 1
|
||||
|
||||
/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
|
||||
#define LV_USE_LIST 1
|
||||
#if LV_USE_LIST != 0
|
||||
/*Default animation time of focusing to a list element [ms] (0: no animation) */
|
||||
# define LV_LIST_DEF_ANIM_TIME 100
|
||||
#endif
|
||||
|
||||
/*Line meter (dependencies: *;)*/
|
||||
#define LV_USE_LINEMETER 1
|
||||
#if LV_USE_LINEMETER
|
||||
/* Draw line more precisely at cost of performance.
|
||||
* Useful if there are lot of lines any minor are visible
|
||||
* 0: No extra precision
|
||||
* 1: Some extra precision
|
||||
* 2: Best precision
|
||||
*/
|
||||
# define LV_LINEMETER_PRECISE 0
|
||||
#endif
|
||||
|
||||
/*Mask (dependencies: -)*/
|
||||
#define LV_USE_OBJMASK 1
|
||||
|
||||
/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
|
||||
#define LV_USE_MSGBOX 1
|
||||
|
||||
/*Page (dependencies: lv_cont)*/
|
||||
#define LV_USE_PAGE 1
|
||||
#if LV_USE_PAGE != 0
|
||||
/*Focus default animation time [ms] (0: no animation)*/
|
||||
# define LV_PAGE_DEF_ANIM_TIME 400
|
||||
#endif
|
||||
|
||||
/*Preload (dependencies: lv_arc, lv_anim)*/
|
||||
#define LV_USE_SPINNER 1
|
||||
#if LV_USE_SPINNER != 0
|
||||
# define LV_SPINNER_DEF_ARC_LENGTH 60 /*[deg]*/
|
||||
# define LV_SPINNER_DEF_SPIN_TIME 1000 /*[ms]*/
|
||||
# define LV_SPINNER_DEF_ANIM LV_SPINNER_TYPE_SPINNING_ARC
|
||||
#endif
|
||||
|
||||
/*Roller (dependencies: lv_ddlist)*/
|
||||
#define LV_USE_ROLLER 1
|
||||
#if LV_USE_ROLLER != 0
|
||||
/*Focus animation time [ms] (0: no animation)*/
|
||||
# define LV_ROLLER_DEF_ANIM_TIME 200
|
||||
|
||||
/*Number of extra "pages" when the roller is infinite*/
|
||||
# define LV_ROLLER_INF_PAGES 7
|
||||
#endif
|
||||
|
||||
/*Slider (dependencies: lv_bar)*/
|
||||
#define LV_USE_SLIDER 1
|
||||
|
||||
/*Spinbox (dependencies: lv_ta)*/
|
||||
#define LV_USE_SPINBOX 1
|
||||
|
||||
/*Switch (dependencies: lv_slider)*/
|
||||
#define LV_USE_SWITCH 1
|
||||
|
||||
/*Text area (dependencies: lv_label, lv_page)*/
|
||||
#define LV_USE_TEXTAREA 1
|
||||
#if LV_USE_TEXTAREA != 0
|
||||
# define LV_TEXTAREA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
|
||||
# define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
|
||||
#endif
|
||||
|
||||
/*Table (dependencies: lv_label)*/
|
||||
#define LV_USE_TABLE 1
|
||||
#if LV_USE_TABLE
|
||||
# define LV_TABLE_COL_MAX 12
|
||||
#endif
|
||||
|
||||
/*Tab (dependencies: lv_page, lv_btnm)*/
|
||||
#define LV_USE_TABVIEW 1
|
||||
# if LV_USE_TABVIEW != 0
|
||||
/*Time of slide animation [ms] (0: no animation)*/
|
||||
# define LV_TABVIEW_DEF_ANIM_TIME 300
|
||||
#endif
|
||||
|
||||
/*Tileview (dependencies: lv_page) */
|
||||
#define LV_USE_TILEVIEW 1
|
||||
#if LV_USE_TILEVIEW
|
||||
/*Time of slide animation [ms] (0: no animation)*/
|
||||
# define LV_TILEVIEW_DEF_ANIM_TIME 300
|
||||
#endif
|
||||
|
||||
/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
|
||||
#define LV_USE_WIN 1
|
||||
|
||||
/*==================
|
||||
* Non-user section
|
||||
*==================*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
/*--END OF LV_CONF_H--*/
|
||||
|
||||
#endif /*LV_CONF_H*/
|
||||
|
||||
#endif /*End of "Content enable"*/
|
18
lvgl-codegen/Cargo.toml
Normal file
18
lvgl-codegen/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "lvgl-codegen"
|
||||
version = "0.5.2"
|
||||
description = "Code generation based on LVGL source code"
|
||||
authors = ["Rafael Caricio <crates.lvgl@caric.io>"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/rafaelcaricio/lvgl-rs"
|
||||
|
||||
[dependencies]
|
||||
regex = "1.4.3"
|
||||
quote = "1.0.9"
|
||||
lazy_static = "1.4.0"
|
||||
proc-macro2 = "1.0.27"
|
||||
Inflector = "0.11.4"
|
||||
syn = { version = "1.0.72", features = ["full"]}
|
||||
|
2
lvgl-codegen/README.md
Normal file
2
lvgl-codegen/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# LVGL Code Generation
|
||||
This is the code generation crate for safe bindings to [`lvgl-rs`](https://github.com/rafaelcaricio/lvgl-rs). This crate is only supposed to be used to build `lvgl-rs`.
|
654
lvgl-codegen/src/lib.rs
Normal file
654
lvgl-codegen/src/lib.rs
Normal file
|
@ -0,0 +1,654 @@
|
|||
use inflector::cases::pascalcase::to_pascal_case;
|
||||
use lazy_static::lazy_static;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use quote::{format_ident, ToTokens};
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use syn::{FnArg, ForeignItem, ForeignItemFn, Item, ReturnType};
|
||||
|
||||
type CGResult<T> = Result<T, Box<dyn Error>>;
|
||||
|
||||
const LIB_PREFIX: &str = "lv_";
|
||||
|
||||
lazy_static! {
|
||||
static ref TYPE_MAPPINGS: HashMap<&'static str, &'static str> = [
|
||||
("u16", "u16"),
|
||||
("i32", "i32"),
|
||||
("u8", "u8"),
|
||||
("bool", "bool"),
|
||||
("* const cty :: c_char", "_"),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum WrapperError {
|
||||
Skip,
|
||||
}
|
||||
|
||||
pub type WrapperResult<T> = Result<T, WrapperError>;
|
||||
|
||||
pub trait Rusty {
|
||||
type Parent;
|
||||
|
||||
fn code(&self, parent: &Self::Parent) -> WrapperResult<TokenStream>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LvWidget {
|
||||
name: String,
|
||||
methods: Vec<LvFunc>,
|
||||
}
|
||||
|
||||
impl Rusty for LvWidget {
|
||||
type Parent = ();
|
||||
|
||||
fn code(&self, _parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
||||
// We don't generate for the generic Obj
|
||||
if self.name.as_str().eq("obj") {
|
||||
return Err(WrapperError::Skip);
|
||||
}
|
||||
|
||||
let widget_name = format_ident!("{}", to_pascal_case(self.name.as_str()));
|
||||
let methods: Vec<TokenStream> = self.methods.iter().flat_map(|m| m.code(self)).collect();
|
||||
Ok(quote! {
|
||||
define_object!(#widget_name);
|
||||
|
||||
impl #widget_name {
|
||||
#(#methods)*
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LvFunc {
|
||||
name: String,
|
||||
args: Vec<LvArg>,
|
||||
ret: Option<LvType>,
|
||||
}
|
||||
|
||||
impl LvFunc {
|
||||
pub fn new(name: String, args: Vec<LvArg>, ret: Option<LvType>) -> Self {
|
||||
Self { name, args, ret }
|
||||
}
|
||||
|
||||
pub fn is_method(&self) -> bool {
|
||||
if !self.args.is_empty() {
|
||||
let first_arg = &self.args[0];
|
||||
return first_arg.typ.literal_name.contains("lv_obj_t");
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Rusty for LvFunc {
|
||||
type Parent = LvWidget;
|
||||
|
||||
fn code(&self, parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
||||
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);
|
||||
let original_func_name = format_ident!("{}", self.name.as_str());
|
||||
|
||||
// generate constructor
|
||||
if new_name.as_str().eq("create") {
|
||||
return Ok(quote! {
|
||||
|
||||
pub fn new<C>(parent: &mut C) -> crate::LvResult<Self>
|
||||
where
|
||||
C: crate::NativeObject,
|
||||
{
|
||||
unsafe {
|
||||
let ptr = lvgl_sys::#original_func_name(parent.raw()?.as_mut(), core::ptr::null_mut());
|
||||
if let Some(raw) = core::ptr::NonNull::new(ptr) {
|
||||
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
||||
Ok(Self { core })
|
||||
} else {
|
||||
Err(crate::LvError::InvalidReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// We don't deal with methods that return types yet
|
||||
if self.ret.is_some() {
|
||||
return Err(WrapperError::Skip);
|
||||
}
|
||||
|
||||
// Make sure all arguments can be generated, skip the first arg (self)!
|
||||
for arg in self.args.iter().skip(1) {
|
||||
arg.code(self)?;
|
||||
}
|
||||
|
||||
let args_decl = self
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(quote!(), |args, (i, arg)| {
|
||||
// if first arg is `const`, then it should be immutable
|
||||
let next_arg = if i == 0 {
|
||||
if arg.get_type().is_const() {
|
||||
quote!(&self)
|
||||
} else {
|
||||
quote!(&mut self)
|
||||
}
|
||||
} else {
|
||||
arg.code(self).unwrap()
|
||||
};
|
||||
if args.is_empty() {
|
||||
quote! {
|
||||
#next_arg
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#args, #next_arg
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let args_processing = self
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(quote!(), |args, (i, arg)| {
|
||||
// if first arg is `const`, then it should be immutable
|
||||
let next_arg = if i == 0 {
|
||||
quote!()
|
||||
} else {
|
||||
let var = arg.get_processing();
|
||||
quote!(#var)
|
||||
};
|
||||
if args.is_empty() {
|
||||
quote! {
|
||||
#next_arg
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#args
|
||||
#next_arg
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let args_call = self
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(quote!(), |args, (i, arg)| {
|
||||
// if first arg is `const`, then it should be immutable
|
||||
let next_arg = if i == 0 {
|
||||
quote!(self.core.raw()?.as_mut())
|
||||
} else {
|
||||
let var = arg.get_value_usage();
|
||||
quote!(#var)
|
||||
};
|
||||
if args.is_empty() {
|
||||
quote! {
|
||||
#next_arg
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#args, #next_arg
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Handle methods that return types
|
||||
Ok(quote! {
|
||||
pub fn #func_name(#args_decl) -> crate::LvResult<()> {
|
||||
#args_processing
|
||||
unsafe {
|
||||
lvgl_sys::#original_func_name(#args_call);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ForeignItemFn> for LvFunc {
|
||||
fn from(ffi: ForeignItemFn) -> Self {
|
||||
let ret = match ffi.sig.output {
|
||||
ReturnType::Default => None,
|
||||
ReturnType::Type(_, typ) => Some(typ.into()),
|
||||
};
|
||||
Self::new(
|
||||
ffi.sig.ident.to_string(),
|
||||
ffi.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|fa| {
|
||||
// Since we know those are foreign functions, we only care about typed arguments
|
||||
if let FnArg::Typed(tya) = fa {
|
||||
Some(tya)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|a| a.clone().into())
|
||||
.collect::<Vec<LvArg>>(),
|
||||
ret,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LvArg {
|
||||
name: String,
|
||||
typ: LvType,
|
||||
}
|
||||
|
||||
impl From<syn::PatType> for LvArg {
|
||||
fn from(fa: syn::PatType) -> Self {
|
||||
Self::new(fa.pat.to_token_stream().to_string(), fa.ty.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl LvArg {
|
||||
pub fn new(name: String, typ: LvType) -> Self {
|
||||
Self { name, typ }
|
||||
}
|
||||
|
||||
pub fn get_name_ident(&self) -> Ident {
|
||||
// Filter Rust language keywords
|
||||
syn::parse_str::<syn::Ident>(self.name.as_str())
|
||||
.unwrap_or_else(|_| format_ident!("r#{}", self.name.as_str()))
|
||||
}
|
||||
|
||||
pub fn get_processing(&self) -> TokenStream {
|
||||
// TODO: A better way to handle this, instead of `is_sometype()`, is using the Rust
|
||||
// type system itself.
|
||||
|
||||
// No need to pre-process this type of argument
|
||||
quote! {}
|
||||
}
|
||||
|
||||
pub fn get_value_usage(&self) -> TokenStream {
|
||||
let ident = self.get_name_ident();
|
||||
if self.typ.is_str() {
|
||||
quote! {
|
||||
#ident.as_ptr()
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#ident
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> &LvType {
|
||||
&self.typ
|
||||
}
|
||||
}
|
||||
|
||||
impl Rusty for LvArg {
|
||||
type Parent = LvFunc;
|
||||
|
||||
fn code(&self, _parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
||||
let name = self.get_name_ident();
|
||||
let typ = self.typ.code(self)?;
|
||||
Ok(quote! {
|
||||
#name: #typ
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LvType {
|
||||
literal_name: String,
|
||||
_r_type: Option<Box<syn::Type>>,
|
||||
}
|
||||
|
||||
impl LvType {
|
||||
pub fn new(literal_name: String) -> Self {
|
||||
Self {
|
||||
literal_name,
|
||||
_r_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(r_type: Box<syn::Type>) -> Self {
|
||||
Self {
|
||||
literal_name: r_type.to_token_stream().to_string(),
|
||||
_r_type: Some(r_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.literal_name.starts_with("const ")
|
||||
}
|
||||
|
||||
pub fn is_str(&self) -> bool {
|
||||
self.literal_name.ends_with("* const cty :: c_char")
|
||||
}
|
||||
}
|
||||
|
||||
impl Rusty for LvType {
|
||||
type Parent = LvArg;
|
||||
|
||||
fn code(&self, _parent: &Self::Parent) -> WrapperResult<TokenStream> {
|
||||
match TYPE_MAPPINGS.get(self.literal_name.as_str()) {
|
||||
Some(name) => {
|
||||
let val = if self.is_str() {
|
||||
quote!(&cstr_core::CStr)
|
||||
} else {
|
||||
let ident = format_ident!("{}", name);
|
||||
quote!(#ident)
|
||||
};
|
||||
Ok(quote! {
|
||||
#val
|
||||
})
|
||||
}
|
||||
None => Err(WrapperError::Skip),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<syn::Type>> for LvType {
|
||||
fn from(t: Box<syn::Type>) -> Self {
|
||||
Self::from(t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CodeGen {
|
||||
functions: Vec<LvFunc>,
|
||||
widgets: Vec<LvWidget>,
|
||||
}
|
||||
|
||||
impl CodeGen {
|
||||
pub fn from(code: &str) -> CGResult<Self> {
|
||||
let functions = Self::load_func_defs(code)?;
|
||||
let widgets = Self::extract_widgets(&functions)?;
|
||||
Ok(Self { functions, widgets })
|
||||
}
|
||||
|
||||
pub fn get_widgets(&self) -> &Vec<LvWidget> {
|
||||
&self.widgets
|
||||
}
|
||||
|
||||
fn extract_widgets(functions: &[LvFunc]) -> CGResult<Vec<LvWidget>> {
|
||||
let widget_names = Self::get_widget_names(functions);
|
||||
|
||||
let widgets = functions.iter().fold(HashMap::new(), |mut ws, f| {
|
||||
for widget_name in &widget_names {
|
||||
if f.name
|
||||
.starts_with(format!("{}{}", LIB_PREFIX, widget_name).as_str())
|
||||
&& f.is_method()
|
||||
{
|
||||
ws.entry(widget_name.clone())
|
||||
.or_insert_with(|| LvWidget {
|
||||
name: widget_name.clone(),
|
||||
methods: Vec::new(),
|
||||
})
|
||||
.methods
|
||||
.push(f.clone())
|
||||
}
|
||||
}
|
||||
ws
|
||||
});
|
||||
|
||||
Ok(widgets.values().cloned().collect())
|
||||
}
|
||||
|
||||
fn get_widget_names(functions: &[LvFunc]) -> Vec<String> {
|
||||
let reg = format!("^{}([^_]+)_create$", LIB_PREFIX);
|
||||
let create_func = Regex::new(reg.as_str()).unwrap();
|
||||
|
||||
functions
|
||||
.iter()
|
||||
.filter(|e| create_func.is_match(e.name.as_str()) && e.args.len() == 2)
|
||||
.map(|f| {
|
||||
String::from(
|
||||
create_func
|
||||
.captures(f.name.as_str())
|
||||
.unwrap()
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub fn load_func_defs(bindgen_code: &str) -> CGResult<Vec<LvFunc>> {
|
||||
let ast: syn::File = syn::parse_str(bindgen_code)?;
|
||||
let fns = ast
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|e| {
|
||||
if let Item::ForeignMod(fm) = e {
|
||||
Some(fm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flat_map(|e| {
|
||||
e.items.into_iter().filter_map(|it| {
|
||||
if let ForeignItem::Fn(f) = it {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.filter(|ff| ff.sig.ident.to_string().starts_with(LIB_PREFIX))
|
||||
.map(|ff| ff.into())
|
||||
.collect::<Vec<LvFunc>>();
|
||||
Ok(fns)
|
||||
}
|
||||
|
||||
pub fn get_function_names(&self) -> CGResult<Vec<String>> {
|
||||
Ok(self.functions.iter().map(|f| f.name.clone()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{CodeGen, LvArg, LvFunc, LvType, LvWidget, Rusty};
|
||||
use quote::quote;
|
||||
|
||||
#[test]
|
||||
fn can_load_bindgen_fns() {
|
||||
let bindgen_code = quote! {
|
||||
extern "C" {
|
||||
#[doc = " Return with the screen of an object"]
|
||||
#[doc = " @param obj pointer to an object"]
|
||||
#[doc = " @return pointer to a screen"]
|
||||
pub fn lv_obj_get_screen(obj: *const lv_obj_t) -> *mut lv_obj_t;
|
||||
}
|
||||
};
|
||||
|
||||
let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap();
|
||||
|
||||
let ffn = cg.get(0).unwrap();
|
||||
assert_eq!(ffn.name, "lv_obj_get_screen");
|
||||
assert_eq!(ffn.args[0].name, "obj");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_identify_widgets_from_function_names() {
|
||||
let funcs = vec![
|
||||
LvFunc::new(
|
||||
"lv_obj_create".to_string(),
|
||||
vec![
|
||||
LvArg::new("parent".to_string(), LvType::new("abc".to_string())),
|
||||
LvArg::new("copy_from".to_string(), LvType::new("bcf".to_string())),
|
||||
],
|
||||
None,
|
||||
),
|
||||
LvFunc::new(
|
||||
"lv_btn_create".to_string(),
|
||||
vec![
|
||||
LvArg::new("parent".to_string(), LvType::new("abc".to_string())),
|
||||
LvArg::new("copy_from".to_string(), LvType::new("bcf".to_string())),
|
||||
],
|
||||
None,
|
||||
),
|
||||
LvFunc::new(
|
||||
"lv_do_something".to_string(),
|
||||
vec![
|
||||
LvArg::new("parent".to_string(), LvType::new("abc".to_string())),
|
||||
LvArg::new("copy_from".to_string(), LvType::new("bcf".to_string())),
|
||||
],
|
||||
None,
|
||||
),
|
||||
LvFunc::new(
|
||||
"lv_invalid_create".to_string(),
|
||||
vec![LvArg::new(
|
||||
"parent".to_string(),
|
||||
LvType::new("abc".to_string()),
|
||||
)],
|
||||
None,
|
||||
),
|
||||
LvFunc::new(
|
||||
"lv_cb_create".to_string(),
|
||||
vec![
|
||||
LvArg::new("parent".to_string(), LvType::new("abc".to_string())),
|
||||
LvArg::new("copy_from".to_string(), LvType::new("bcf".to_string())),
|
||||
],
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
let widget_names = CodeGen::get_widget_names(&funcs);
|
||||
|
||||
assert_eq!(widget_names.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_method_wrapper() {
|
||||
// pub fn lv_arc_set_bg_end_angle(arc: *mut lv_obj_t, end: u16);
|
||||
let arc_set_bg_end_angle = LvFunc::new(
|
||||
"lv_arc_set_bg_end_angle".to_string(),
|
||||
vec![
|
||||
LvArg::new("arc".to_string(), LvType::new("*mut lv_obj_t".to_string())),
|
||||
LvArg::new("end".to_string(), LvType::new("u16".to_string())),
|
||||
],
|
||||
None,
|
||||
);
|
||||
let arc_widget = LvWidget {
|
||||
name: "arc".to_string(),
|
||||
methods: vec![],
|
||||
};
|
||||
|
||||
let code = arc_set_bg_end_angle.code(&arc_widget).unwrap();
|
||||
let expected_code = quote! {
|
||||
pub fn set_bg_end_angle(&mut self, end: u16) -> crate::LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_arc_set_bg_end_angle(self.core.raw()?.as_mut(), end);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(code.to_string(), expected_code.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_method_wrapper_for_str_types_as_argument() {
|
||||
let bindgen_code = quote! {
|
||||
extern "C" {
|
||||
#[doc = " Set a new text for a label. Memory will be allocated to store the text by the label."]
|
||||
#[doc = " @param label pointer to a label object"]
|
||||
#[doc = " @param text '\\0' terminated character string. NULL to refresh with the current text."]
|
||||
pub fn lv_label_set_text(label: *mut lv_obj_t, text: *const cty::c_char);
|
||||
}
|
||||
};
|
||||
let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap();
|
||||
|
||||
let label_set_text = cg.get(0).unwrap().clone();
|
||||
let parent_widget = LvWidget {
|
||||
name: "label".to_string(),
|
||||
methods: vec![],
|
||||
};
|
||||
|
||||
let code = label_set_text.code(&parent_widget).unwrap();
|
||||
let expected_code = quote! {
|
||||
|
||||
pub fn set_text(&mut self, text: &cstr_core::CStr) -> crate::LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_label_set_text(
|
||||
self.core.raw()?.as_mut(),
|
||||
text.as_ptr()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
assert_eq!(code.to_string(), expected_code.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_basic_widget_code() {
|
||||
let arc_widget = LvWidget {
|
||||
name: "arc".to_string(),
|
||||
methods: vec![],
|
||||
};
|
||||
|
||||
let code = arc_widget.code(&()).unwrap();
|
||||
let expected_code = quote! {
|
||||
define_object!(Arc);
|
||||
|
||||
impl Arc {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(code.to_string(), expected_code.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_widget_with_constructor_code() {
|
||||
// pub fn lv_arc_create(par: *mut lv_obj_t, copy: *const lv_obj_t) -> *mut lv_obj_t;
|
||||
let arc_create = LvFunc::new(
|
||||
"lv_arc_create".to_string(),
|
||||
vec![
|
||||
LvArg::new("par".to_string(), LvType::new("*mut lv_obj_t".to_string())),
|
||||
LvArg::new(
|
||||
"copy".to_string(),
|
||||
LvType::new("*const lv_obj_t".to_string()),
|
||||
),
|
||||
],
|
||||
Some(LvType::new("*mut lv_obj_t".to_string())),
|
||||
);
|
||||
|
||||
let arc_widget = LvWidget {
|
||||
name: "arc".to_string(),
|
||||
methods: vec![arc_create],
|
||||
};
|
||||
|
||||
let code = arc_widget.code(&()).unwrap();
|
||||
let expected_code = quote! {
|
||||
define_object!(Arc);
|
||||
|
||||
impl Arc {
|
||||
pub fn new<C>(parent: &mut C) -> crate::LvResult<Self>
|
||||
where
|
||||
C: crate::NativeObject,
|
||||
{
|
||||
|
||||
unsafe {
|
||||
let ptr = lvgl_sys::lv_arc_create(parent.raw()?.as_mut(), core::ptr::null_mut());
|
||||
if let Some(raw) = core::ptr::NonNull::new(ptr) {
|
||||
let core = <crate::Obj as crate::Widget>::from_raw(raw);
|
||||
Ok(Self { core })
|
||||
} else {
|
||||
Err(crate::LvError::InvalidReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(code.to_string(), expected_code.to_string());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "lvgl-sys"
|
||||
description = "Raw bindings to the LittlevGL C library."
|
||||
version = "0.2.0"
|
||||
description = "Raw bindings to the LVGL C library."
|
||||
version = "0.5.2"
|
||||
authors = ["Rafael Caricio <crates.lvgl-sys@caric.io>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
@ -20,5 +20,8 @@ name = "lvgl_sys"
|
|||
cty = "0.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.50"
|
||||
bindgen = "0.53.2"
|
||||
cc = "1.0.68"
|
||||
bindgen = "0.59.2"
|
||||
|
||||
[features]
|
||||
use-vendored-config = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,41 +1,53 @@
|
|||
use bindgen;
|
||||
use cc::Build;
|
||||
use std::{env, path::Path, path::PathBuf};
|
||||
|
||||
static CONFIG_NAME: &str = "DEP_LV_CONFIG_PATH";
|
||||
|
||||
fn main() {
|
||||
let project_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let project_dir = canonicalize(PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()));
|
||||
let shims_dir = project_dir.join("shims");
|
||||
let vendor = project_dir.join("vendor");
|
||||
let vendor_src = vendor.join("lvgl").join("src");
|
||||
|
||||
let lv_config_dir = {
|
||||
let raw_path = env::var(CONFIG_NAME).expect(
|
||||
format!(
|
||||
"The environment variable {} is required to be defined",
|
||||
CONFIG_NAME
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
let conf_path = PathBuf::from(raw_path);
|
||||
let conf_path = env::var(CONFIG_NAME)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| {
|
||||
match std::env::var("DOCS_RS") {
|
||||
Ok(_) => {
|
||||
// We've detected that we are building for docs.rs
|
||||
// so let's use the vendored `lv_conf.h` file.
|
||||
vendor.join("include")
|
||||
}
|
||||
Err(_) => {
|
||||
#[cfg(not(feature = "use-vendored-config"))]
|
||||
panic!(
|
||||
"The environment variable {} is required to be defined",
|
||||
CONFIG_NAME
|
||||
);
|
||||
|
||||
#[cfg(feature = "use-vendored-config")]
|
||||
vendor.join("include")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if !conf_path.exists() {
|
||||
panic!(format!(
|
||||
"Directory referenced by {} needs to exist",
|
||||
panic!(
|
||||
"Directory {} referenced by {} needs to exist",
|
||||
conf_path.to_string_lossy(),
|
||||
CONFIG_NAME
|
||||
));
|
||||
);
|
||||
}
|
||||
if !conf_path.is_dir() {
|
||||
panic!(format!("{} needs to be a directory", CONFIG_NAME));
|
||||
panic!("{} needs to be a directory", CONFIG_NAME);
|
||||
}
|
||||
if !conf_path.join("lv_conf.h").exists() {
|
||||
panic!(format!(
|
||||
"Directory referenced by {} needs to contain a file called lv_conf.h",
|
||||
panic!(
|
||||
"Directory {} referenced by {} needs to contain a file called lv_conf.h",
|
||||
conf_path.to_string_lossy(),
|
||||
CONFIG_NAME
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
|
@ -49,41 +61,73 @@ fn main() {
|
|||
add_c_files(&mut cfg, vendor_src.join("lv_core"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_draw"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_font"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_gpu"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_hal"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_misc"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_objx"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_themes"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_themes"));
|
||||
add_c_files(&mut cfg, vendor_src.join("lv_widgets"));
|
||||
add_c_files(&mut cfg, &lv_config_dir);
|
||||
add_c_files(&mut cfg, &shims_dir);
|
||||
|
||||
cfg.define("LV_CONF_INCLUDE_SIMPLE", Some("1"))
|
||||
.include(&vendor_src)
|
||||
.include(&vendor)
|
||||
.file("string.c")
|
||||
.warnings(false)
|
||||
.include(&lv_config_dir)
|
||||
.compile("lvgl");
|
||||
|
||||
let cc_args = [
|
||||
let mut cc_args = vec![
|
||||
"-DLV_CONF_INCLUDE_SIMPLE=1",
|
||||
"-I",
|
||||
lv_config_dir.to_str().unwrap(),
|
||||
"-I",
|
||||
vendor.to_str().unwrap(),
|
||||
"-fvisibility=default",
|
||||
];
|
||||
|
||||
// Set correct target triple for bindgen when cross-compiling
|
||||
let target = env::var("TARGET").expect("Cargo build scripts always have TARGET");
|
||||
let host = env::var("HOST").expect("Cargo build scripts always have HOST");
|
||||
if target != host {
|
||||
cc_args.push("-target");
|
||||
cc_args.push(target.as_str());
|
||||
}
|
||||
|
||||
let mut additional_args = Vec::new();
|
||||
if target.ends_with("emscripten") {
|
||||
if let Ok(em_path) = env::var("EMSDK") {
|
||||
additional_args.push("-I".to_string());
|
||||
additional_args.push(format!(
|
||||
"{}/upstream/emscripten/system/include/libc",
|
||||
em_path
|
||||
));
|
||||
additional_args.push("-I".to_string());
|
||||
additional_args.push(format!(
|
||||
"{}/upstream/emscripten/system/lib/libc/musl/arch/emscripten",
|
||||
em_path
|
||||
));
|
||||
additional_args.push("-I".to_string());
|
||||
additional_args.push(format!(
|
||||
"{}/upstream/emscripten/system/include/SDL",
|
||||
em_path
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindgen::Builder::default()
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header(shims_dir.join("lvgl_sys.h").to_str().unwrap())
|
||||
.generate_comments(false)
|
||||
.layout_tests(false)
|
||||
.use_core()
|
||||
.rustfmt_bindings(true)
|
||||
.ctypes_prefix("cty")
|
||||
.raw_line("use cty;")
|
||||
.clang_args(&cc_args)
|
||||
.clang_args(&additional_args)
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Can't write bindings!");
|
||||
}
|
||||
|
@ -99,3 +143,10 @@ fn add_c_files(build: &mut cc::Build, path: impl AsRef<Path>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
|
||||
let canonicalized = path.as_ref().canonicalize().unwrap();
|
||||
let canonicalized = &*canonicalized.to_string_lossy();
|
||||
|
||||
PathBuf::from(canonicalized.strip_prefix(r"\\?\").unwrap_or(canonicalized))
|
||||
}
|
||||
|
|
|
@ -2,9 +2,17 @@
|
|||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::redundant_static_lifetimes)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
pub fn _bindgen_raw_src() -> &'static str {
|
||||
include_str!(concat!(env!("OUT_DIR"), "/bindings.rs"))
|
||||
}
|
||||
|
||||
mod string_impl;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -14,11 +22,11 @@ mod tests {
|
|||
unsafe {
|
||||
lv_init();
|
||||
|
||||
let horizontal_resolution = lv_disp_get_hor_res(std::ptr::null_mut());
|
||||
assert_eq!(horizontal_resolution, 480);
|
||||
let horizontal_resolution = lv_disp_get_hor_res(core::ptr::null_mut());
|
||||
assert_eq!(horizontal_resolution, LV_HOR_RES_MAX as i16);
|
||||
|
||||
let vertical_resolution = lv_disp_get_ver_res(std::ptr::null_mut());
|
||||
assert_eq!(vertical_resolution, 320);
|
||||
let vertical_resolution = lv_disp_get_ver_res(core::ptr::null_mut());
|
||||
assert_eq!(vertical_resolution, LV_VER_RES_MAX as i16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
159
lvgl-sys/src/string_impl.rs
Normal file
159
lvgl-sys/src/string_impl.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2018 Redox OS
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use core::{ptr, usize};
|
||||
use cty::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strchr(mut s: *const c_char, c: c_int) -> *mut c_char {
|
||||
let c = c as c_char;
|
||||
while *s != 0 {
|
||||
if *s == c {
|
||||
return s as *mut c_char;
|
||||
}
|
||||
s = s.offset(1);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
|
||||
strncmp(s1, s2, usize::MAX)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strcoll(s1: *const c_char, s2: *const c_char) -> c_int {
|
||||
// relibc has no locale stuff (yet)
|
||||
strcmp(s1, s2)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char {
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
let byte = *src.offset(i);
|
||||
*dst.offset(i) = byte;
|
||||
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strlen(s: *const c_char) -> size_t {
|
||||
strnlen(s, usize::MAX)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t {
|
||||
let mut i = 0;
|
||||
while i < size {
|
||||
if *s.add(i) == 0 {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
i as size_t
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strnlen_s(s: *const c_char, size: size_t) -> size_t {
|
||||
if s.is_null() {
|
||||
0
|
||||
} else {
|
||||
strnlen(s, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strcat(s1: *mut c_char, s2: *const c_char) -> *mut c_char {
|
||||
strncat(s1, s2, usize::MAX)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncat(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char {
|
||||
let len = strlen(s1 as *const c_char);
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let b = *s2.add(i);
|
||||
if b == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
*s1.add(len + i) = b;
|
||||
i += 1;
|
||||
}
|
||||
*s1.add(len + i) = 0;
|
||||
|
||||
s1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int {
|
||||
let s1 = core::slice::from_raw_parts(s1 as *const c_uchar, n);
|
||||
let s2 = core::slice::from_raw_parts(s2 as *const c_uchar, n);
|
||||
|
||||
for (&a, &b) in s1.iter().zip(s2.iter()) {
|
||||
let val = (a as c_int) - (b as c_int);
|
||||
if a != b || a == 0 {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncpy(dst: *mut c_char, src: *const c_char, n: size_t) -> *mut c_char {
|
||||
let mut i = 0;
|
||||
|
||||
while *src.add(i) != 0 && i < n {
|
||||
*dst.add(i) = *src.add(i);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
for i in i..n {
|
||||
*dst.add(i) = 0;
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char {
|
||||
let len = strlen(s) as isize;
|
||||
let c = c as c_char;
|
||||
let mut i = len - 1;
|
||||
while i >= 0 {
|
||||
if *s.offset(i) == c {
|
||||
return s.offset(i) as *mut c_char;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/lib/string.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
size_t strspn(const char *s, const char *accept)
|
||||
{
|
||||
const char *p;
|
||||
const char *a;
|
||||
size_t count = 0;
|
||||
|
||||
for (p = s; *p != '\0'; ++p) {
|
||||
for (a = accept; *a != '\0'; ++a) {
|
||||
if (*p == *a)
|
||||
break;
|
||||
}
|
||||
if (*a == '\0')
|
||||
return count;
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t strcspn(const char *s, const char *reject)
|
||||
{
|
||||
const char *p;
|
||||
const char *r;
|
||||
size_t count = 0;
|
||||
|
||||
for (p = s; *p != '\0'; ++p) {
|
||||
for (r = reject; *r != '\0'; ++r) {
|
||||
if (*p == *r)
|
||||
return count;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
for (sc = s; *sc != '\0'; ++sc)
|
||||
/* nothing */;
|
||||
return sc - s;
|
||||
}
|
||||
|
||||
char *strchr(const char *s, int c)
|
||||
{
|
||||
for (; *s != (char)c; ++s)
|
||||
if (*s == '\0')
|
||||
return NULL;
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
char *strcpy(char *dest, const char *src)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
while ((*dest++ = *src++) != '\0')
|
||||
/* nothing */;
|
||||
return tmp;
|
||||
}
|
|
@ -1,3 +1,14 @@
|
|||
/**
|
||||
* @file lv_conf.h
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER
|
||||
*/
|
||||
|
||||
#if 1 /*Set it to "1" to enable content*/
|
||||
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_H
|
||||
/* clang-format off */
|
||||
|
@ -14,7 +25,7 @@
|
|||
|
||||
/* Color depth:
|
||||
* - 1: 1 byte per pixel
|
||||
* - 8: RGB233
|
||||
* - 8: RGB332
|
||||
* - 16: RGB565
|
||||
* - 32: ARGB8888
|
||||
*/
|
||||
|
@ -32,9 +43,6 @@
|
|||
/*Images pixels with this color will not be drawn (with chroma keying)*/
|
||||
#define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/
|
||||
|
||||
/* Enable chroma keying for indexed images. */
|
||||
#define LV_INDEXED_CHROMA 1
|
||||
|
||||
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
|
||||
#define LV_ANTIALIAS 1
|
||||
|
||||
|
@ -45,7 +53,18 @@
|
|||
/* Dot Per Inch: used to initialize default sizes.
|
||||
* E.g. a button with width = LV_DPI / 2 -> half inch wide
|
||||
* (Not so important, you can adjust it to modify default sizes and spaces)*/
|
||||
#define LV_DPI 298 /*[px]*/
|
||||
#define LV_DPI 130 /*[px]*/
|
||||
|
||||
/* The the real width of the display changes some default values:
|
||||
* default object sizes, layout of examples, etc.
|
||||
* According to the width of the display (hor. res. / dpi)
|
||||
* the displays fall in 4 categories.
|
||||
* The 4th is extra large which has no upper limit so not listed here
|
||||
* The upper limit of the categories are set below in 0.1 inch unit.
|
||||
*/
|
||||
#define LV_DISP_SMALL_LIMIT 30
|
||||
#define LV_DISP_MEDIUM_LIMIT 50
|
||||
#define LV_DISP_LARGE_LIMIT 70
|
||||
|
||||
/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */
|
||||
typedef int16_t lv_coord_t;
|
||||
|
@ -61,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 (4U * 1024U)
|
||||
# define LV_MEM_SIZE (32U * 1024U)
|
||||
|
||||
/* Complier prefix for a big array declaration */
|
||||
# define LV_MEM_ATTR
|
||||
|
@ -101,7 +120,7 @@ typedef int16_t lv_coord_t;
|
|||
#define LV_INDEV_DEF_DRAG_LIMIT 10
|
||||
|
||||
/* Drag throw slow-down in [%]. Greater value -> faster slow-down */
|
||||
#define LV_INDEV_DEF_DRAG_THROW 20
|
||||
#define LV_INDEV_DEF_DRAG_THROW 10
|
||||
|
||||
/* Long press time in milliseconds.
|
||||
* Time to send `LV_EVENT_LONG_PRESSSED`) */
|
||||
|
@ -111,6 +130,13 @@ typedef int16_t lv_coord_t;
|
|||
* Time between `LV_EVENT_LONG_PRESSED_REPEAT */
|
||||
#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
|
||||
|
||||
|
||||
/* Gesture threshold in pixels */
|
||||
#define LV_INDEV_DEF_GESTURE_LIMIT 50
|
||||
|
||||
/* Gesture min velocity at release before swipe (pixels)*/
|
||||
#define LV_INDEV_DEF_GESTURE_MIN_VELOCITY 3
|
||||
|
||||
/*==================
|
||||
* Feature usage
|
||||
*==================*/
|
||||
|
@ -126,6 +152,22 @@ typedef void * lv_anim_user_data_t;
|
|||
|
||||
/* 1: Enable shadow drawing*/
|
||||
#define LV_USE_SHADOW 1
|
||||
#if LV_USE_SHADOW
|
||||
/* Allow buffering some shadow calculation
|
||||
* LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer,
|
||||
* where shadow size is `shadow_width + radius`
|
||||
* Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
|
||||
#define LV_SHADOW_CACHE_SIZE 0
|
||||
#endif
|
||||
|
||||
/* 1: Use other blend modes than normal (`LV_BLEND_MODE_...`)*/
|
||||
#define LV_USE_BLEND_MODES 1
|
||||
|
||||
/* 1: Use the `opa_scale` style property to set the opacity of an object and its children at once*/
|
||||
#define LV_USE_OPA_SCALE 1
|
||||
|
||||
/* 1: Use image zoom and rotation*/
|
||||
#define LV_USE_IMG_TRANSFORM 1
|
||||
|
||||
/* 1: Enable object groups (for keyboard/encoder navigation) */
|
||||
#define LV_USE_GROUP 1
|
||||
|
@ -134,10 +176,11 @@ typedef void * lv_group_user_data_t;
|
|||
#endif /*LV_USE_GROUP*/
|
||||
|
||||
/* 1: Enable GPU interface*/
|
||||
#define LV_USE_GPU 0
|
||||
#define LV_USE_GPU 1 /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
|
||||
#define LV_USE_GPU_STM32_DMA2D 0
|
||||
|
||||
/* 1: Enable file system (might be required for images */
|
||||
#define LV_USE_FILESYSTEM 0
|
||||
#define LV_USE_FILESYSTEM 1
|
||||
#if LV_USE_FILESYSTEM
|
||||
/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_fs_drv_user_data_t;
|
||||
|
@ -146,6 +189,12 @@ typedef void * lv_fs_drv_user_data_t;
|
|||
/*1: Add a `user_data` to drivers and objects*/
|
||||
#define LV_USE_USER_DATA 1
|
||||
|
||||
/*1: Show CPU usage and FPS count in the right bottom corner*/
|
||||
#define LV_USE_PERF_MONITOR 0
|
||||
|
||||
/*1: Use the functions and types from the older API if possible */
|
||||
#define LV_USE_API_EXTENSION_V6 1
|
||||
|
||||
/*========================
|
||||
* Image decoder and cache
|
||||
*========================*/
|
||||
|
@ -176,6 +225,9 @@ typedef void * lv_img_decoder_user_data_t;
|
|||
/* Define a custom attribute to `lv_task_handler` function */
|
||||
#define LV_ATTRIBUTE_TASK_HANDLER
|
||||
|
||||
/* Define a custom attribute to `lv_disp_flush_ready` function */
|
||||
#define LV_ATTRIBUTE_FLUSH_READY
|
||||
|
||||
/* With size optimization (-Os) the compiler might not align data to
|
||||
* 4 or 8 byte boundary. This alignment will be explicitly applied where needed.
|
||||
* E.g. __attribute__((aligned(4))) */
|
||||
|
@ -185,6 +237,10 @@ typedef void * lv_img_decoder_user_data_t;
|
|||
* font's bitmaps */
|
||||
#define LV_ATTRIBUTE_LARGE_CONST
|
||||
|
||||
/* Prefix performance critical functions to place them into a faster memory (e.g RAM)
|
||||
* Uses 15-20 kB extra memory */
|
||||
#define LV_ATTRIBUTE_FAST_MEM
|
||||
|
||||
/* Export integer constant to binding.
|
||||
* This macro is used with constants in the form of LV_<CONST> that
|
||||
* should also appear on lvgl binding API such as Micropython
|
||||
|
@ -242,7 +298,7 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
|
|||
* The behavior of asserts can be overwritten by redefining them here.
|
||||
* E.g. #define LV_ASSERT_MEM(p) <my_assert_code>
|
||||
*/
|
||||
#define LV_USE_DEBUG 0
|
||||
#define LV_USE_DEBUG 1
|
||||
#if LV_USE_DEBUG
|
||||
|
||||
/*Check if the parameter is NULL. (Quite fast) */
|
||||
|
@ -251,6 +307,9 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
|
|||
/*Checks is the memory is successfully allocated or no. (Quite fast)*/
|
||||
#define LV_USE_ASSERT_MEM 1
|
||||
|
||||
/*Check the integrity of `lv_mem` after critical operations. (Slow)*/
|
||||
#define LV_USE_ASSERT_MEM_INTEGRITY 0
|
||||
|
||||
/* Check the strings.
|
||||
* Search for NULL, very long strings, invalid characters, and unnatural repetitions. (Slow)
|
||||
* If disabled `LV_USE_ASSERT_NULL` will be performed instead (if it's enabled) */
|
||||
|
@ -261,44 +320,47 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
|
|||
#define LV_USE_ASSERT_OBJ 0
|
||||
|
||||
/*Check if the styles are properly initialized. (Fast)*/
|
||||
#define LV_USE_ASSERT_STYLE 1
|
||||
#define LV_USE_ASSERT_STYLE 0
|
||||
|
||||
#endif /*LV_USE_DEBUG*/
|
||||
|
||||
/*================
|
||||
* THEME USAGE
|
||||
*================*/
|
||||
#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
|
||||
|
||||
#define LV_USE_THEME_TEMPL 0 /*Just for test*/
|
||||
#define LV_USE_THEME_DEFAULT 0 /*Built mainly from the built-in styles. Consumes very few RAM*/
|
||||
#define LV_USE_THEME_ALIEN 0 /*Dark futuristic theme*/
|
||||
#define LV_USE_THEME_NIGHT 0 /*Dark elegant theme*/
|
||||
#define LV_USE_THEME_MONO 0 /*Mono color theme for monochrome displays*/
|
||||
#define LV_USE_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/
|
||||
#define LV_USE_THEME_ZEN 0 /*Peaceful, mainly light theme */
|
||||
#define LV_USE_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/
|
||||
|
||||
/*==================
|
||||
* FONT USAGE
|
||||
*===================*/
|
||||
|
||||
/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel.
|
||||
* The symbols are available via `LV_SYMBOL_...` defines
|
||||
* More info about fonts: https://docs.littlevgl.com/#Fonts
|
||||
* To create a new font go to: https://littlevgl.com/ttf-font-to-c-array
|
||||
* More info about fonts: https://docs.lvgl.com/#Fonts
|
||||
* To create a new font go to: https://lvgl.com/ttf-font-to-c-array
|
||||
*/
|
||||
|
||||
/* Robot fonts with bpp = 4
|
||||
* https://fonts.google.com/specimen/Roboto */
|
||||
#define LV_FONT_ROBOTO_12 0
|
||||
#define LV_FONT_ROBOTO_16 0
|
||||
#define LV_FONT_ROBOTO_22 0
|
||||
#define LV_FONT_ROBOTO_28 1
|
||||
/* Montserrat fonts with bpp = 4
|
||||
* https://fonts.google.com/specimen/Montserrat */
|
||||
#define LV_FONT_MONTSERRAT_12 0
|
||||
#define LV_FONT_MONTSERRAT_14 0
|
||||
#define LV_FONT_MONTSERRAT_16 1
|
||||
#define LV_FONT_MONTSERRAT_18 0
|
||||
#define LV_FONT_MONTSERRAT_20 0
|
||||
#define LV_FONT_MONTSERRAT_22 0
|
||||
#define LV_FONT_MONTSERRAT_24 0
|
||||
#define LV_FONT_MONTSERRAT_26 0
|
||||
#define LV_FONT_MONTSERRAT_28 0
|
||||
#define LV_FONT_MONTSERRAT_30 0
|
||||
#define LV_FONT_MONTSERRAT_32 0
|
||||
#define LV_FONT_MONTSERRAT_34 0
|
||||
#define LV_FONT_MONTSERRAT_36 0
|
||||
#define LV_FONT_MONTSERRAT_38 0
|
||||
#define LV_FONT_MONTSERRAT_40 0
|
||||
#define LV_FONT_MONTSERRAT_42 0
|
||||
#define LV_FONT_MONTSERRAT_44 0
|
||||
#define LV_FONT_MONTSERRAT_46 0
|
||||
#define LV_FONT_MONTSERRAT_48 0
|
||||
|
||||
/* Demonstrate special features */
|
||||
#define LV_FONT_ROBOTO_12_SUBPX 1
|
||||
#define LV_FONT_ROBOTO_28_COMPRESSED 1 /*bpp = 3*/
|
||||
#define LV_FONT_MONTSERRAT_12_SUBPX 0
|
||||
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
|
||||
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, PErisan letters and all their forms*/
|
||||
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
|
||||
|
||||
/*Pixel perfect monospace font
|
||||
* http://pelulamu.net/unscii/ */
|
||||
|
@ -312,9 +374,6 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
|
|||
*/
|
||||
#define LV_FONT_CUSTOM_DECLARE
|
||||
|
||||
/*Always set a default font from the built-in fonts*/
|
||||
#define LV_FONT_DEFAULT &lv_font_roboto_28
|
||||
|
||||
/* Enable it if you have fonts with a lot of characters.
|
||||
* The limit depends on the font size, font face and bpp
|
||||
* but with > 10,000 characters if you see issues probably you need to enable it.*/
|
||||
|
@ -329,6 +388,43 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
|
|||
/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_font_user_data_t;
|
||||
|
||||
/*================
|
||||
* THEME USAGE
|
||||
*================*/
|
||||
|
||||
/*Always enable at least on theme*/
|
||||
|
||||
/* No theme, you can apply your styles as you need
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_EMPTY 1
|
||||
|
||||
/*Simple to the create your theme based on it
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_TEMPLATE 1
|
||||
|
||||
/* A fast and impressive theme.
|
||||
* Flags:
|
||||
* LV_THEME_MATERIAL_FLAG_LIGHT: light theme
|
||||
* LV_THEME_MATERIAL_FLAG_DARK: dark theme*/
|
||||
#define LV_USE_THEME_MATERIAL 1
|
||||
|
||||
/* Mono-color theme for monochrome displays.
|
||||
* If LV_THEME_DEFAULT_COLOR_PRIMARY is LV_COLOR_BLACK the
|
||||
* texts and borders will be black and the background will be
|
||||
* white. Else the colors are inverted.
|
||||
* No flags. Set LV_THEME_DEFAULT_FLAG 0 */
|
||||
#define LV_USE_THEME_MONO 1
|
||||
|
||||
#define LV_THEME_DEFAULT_INCLUDE <stdint.h> /*Include a header for the init. function*/
|
||||
#define LV_THEME_DEFAULT_INIT lv_theme_material_init
|
||||
#define LV_THEME_DEFAULT_COLOR_PRIMARY LV_COLOR_RED
|
||||
#define LV_THEME_DEFAULT_COLOR_SECONDARY LV_COLOR_BLUE
|
||||
#define LV_THEME_DEFAULT_FLAG LV_THEME_MATERIAL_FLAG_LIGHT
|
||||
#define LV_THEME_DEFAULT_FONT_SMALL &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_NORMAL &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_SUBTITLE &lv_font_montserrat_16
|
||||
#define LV_THEME_DEFAULT_FONT_TITLE &lv_font_montserrat_16
|
||||
|
||||
/*=================
|
||||
* Text settings
|
||||
*=================*/
|
||||
|
@ -340,13 +436,12 @@ typedef void * lv_font_user_data_t;
|
|||
* */
|
||||
#define LV_TXT_ENC LV_TXT_ENC_UTF8
|
||||
|
||||
/*Can break (wrap) texts on these chars*/
|
||||
/*Can break (wrap) texts on these chars*/
|
||||
#define LV_TXT_BREAK_CHARS " ,.;:-_"
|
||||
|
||||
|
||||
/* If a word is at least this long, will break wherever "prettiest"
|
||||
* To disable, set to a value <= 0 */
|
||||
#define LV_TXT_LINE_BREAK_LONG_LEN 12
|
||||
#define LV_TXT_LINE_BREAK_LONG_LEN 0
|
||||
|
||||
/* Minimum number of characters in a long word to put on a line before a break.
|
||||
* Depends on LV_TXT_LINE_BREAK_LONG_LEN. */
|
||||
|
@ -372,20 +467,36 @@ typedef void * lv_font_user_data_t;
|
|||
#define LV_BIDI_BASE_DIR_DEF LV_BIDI_DIR_AUTO
|
||||
#endif
|
||||
|
||||
/* Enable Arabic/Persian processing
|
||||
* In these languages characters should be replaced with
|
||||
* an other form based on their position in the text */
|
||||
#define LV_USE_ARABIC_PERSIAN_CHARS 0
|
||||
|
||||
/*Change the built in (v)snprintf functions*/
|
||||
#define LV_SPRINTF_CUSTOM 0
|
||||
#if LV_SPRINTF_CUSTOM
|
||||
# define LV_SPRINTF_INCLUDE <stdio.h>
|
||||
# define lv_snprintf snprintf
|
||||
# define lv_vsnprintf vsnprintf
|
||||
#else /*!LV_SPRINTF_CUSTOM*/
|
||||
# define LV_SPRINTF_DISABLE_FLOAT 1
|
||||
#endif /*LV_SPRINTF_CUSTOM*/
|
||||
|
||||
/*===================
|
||||
* LV_OBJ SETTINGS
|
||||
*==================*/
|
||||
|
||||
#if LV_USE_USER_DATA
|
||||
/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/
|
||||
typedef void * lv_obj_user_data_t;
|
||||
/*Provide a function to free user data*/
|
||||
#define LV_USE_USER_DATA_FREE 0
|
||||
#if LV_USE_USER_DATA_FREE
|
||||
# define LV_USER_DATA_FREE_INCLUDE "something.h" /*Header for user data free function*/
|
||||
/* Function prototype : void user_data_free(lv_obj_t * obj); */
|
||||
# define LV_USER_DATA_FREE (user_data_free) /*Invoking for user data free function*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/
|
||||
#define LV_USE_OBJ_REALIGN 1
|
||||
|
@ -395,13 +506,13 @@ typedef void * lv_obj_user_data_t;
|
|||
* LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px)
|
||||
* LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px)
|
||||
*/
|
||||
#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_OFF
|
||||
#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_TINY
|
||||
|
||||
/*==================
|
||||
* LV OBJ X USAGE
|
||||
*================*/
|
||||
/*
|
||||
* Documentation of the object types: https://docs.littlevgl.com/#Object-types
|
||||
* Documentation of the object types: https://docs.lvgl.com/#Object-types
|
||||
*/
|
||||
|
||||
/*Arc (dependencies: -)*/
|
||||
|
@ -412,13 +523,9 @@ typedef void * lv_obj_user_data_t;
|
|||
|
||||
/*Button (dependencies: lv_cont*/
|
||||
#define LV_USE_BTN 1
|
||||
#if LV_USE_BTN != 0
|
||||
/*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/
|
||||
# define LV_BTN_INK_EFFECT 0
|
||||
#endif
|
||||
|
||||
/*Button matrix (dependencies: -)*/
|
||||
#define LV_USE_BTNM 1
|
||||
#define LV_USE_BTNMATRIX 1
|
||||
|
||||
/*Calendar (dependencies: -)*/
|
||||
#define LV_USE_CALENDAR 1
|
||||
|
@ -427,12 +534,12 @@ typedef void * lv_obj_user_data_t;
|
|||
#define LV_USE_CANVAS 1
|
||||
|
||||
/*Check box (dependencies: lv_btn, lv_label)*/
|
||||
#define LV_USE_CB 1
|
||||
#define LV_USE_CHECKBOX 1
|
||||
|
||||
/*Chart (dependencies: -)*/
|
||||
#define LV_USE_CHART 1
|
||||
#if LV_USE_CHART
|
||||
# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 20
|
||||
# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 256
|
||||
#endif
|
||||
|
||||
/*Container (dependencies: -*/
|
||||
|
@ -442,13 +549,13 @@ typedef void * lv_obj_user_data_t;
|
|||
#define LV_USE_CPICKER 1
|
||||
|
||||
/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
|
||||
#define LV_USE_DDLIST 1
|
||||
#if LV_USE_DDLIST != 0
|
||||
#define LV_USE_DROPDOWN 1
|
||||
#if LV_USE_DROPDOWN != 0
|
||||
/*Open and close default animation time [ms] (0: no animation)*/
|
||||
# define LV_DDLIST_DEF_ANIM_TIME 200
|
||||
# define LV_DROPDOWN_DEF_ANIM_TIME 200
|
||||
#endif
|
||||
|
||||
/*Gauge (dependencies:lv_bar, lv_lmeter)*/
|
||||
/*Gauge (dependencies:lv_bar, lv_linemeter)*/
|
||||
#define LV_USE_GAUGE 1
|
||||
|
||||
/*Image (dependencies: lv_label*/
|
||||
|
@ -462,7 +569,7 @@ typedef void * lv_obj_user_data_t;
|
|||
#endif
|
||||
|
||||
/*Keyboard (dependencies: lv_btnm)*/
|
||||
#define LV_USE_KB 1
|
||||
#define LV_USE_KEYBOARD 1
|
||||
|
||||
/*Label (dependencies: -*/
|
||||
#define LV_USE_LABEL 1
|
||||
|
@ -482,6 +589,10 @@ typedef void * lv_obj_user_data_t;
|
|||
|
||||
/*LED (dependencies: -)*/
|
||||
#define LV_USE_LED 1
|
||||
#if LV_USE_LED
|
||||
# define LV_LED_BRIGHT_MIN 120 /*Minimal brightness*/
|
||||
# define LV_LED_BRIGHT_MAX 255 /*Maximal brightness*/
|
||||
#endif
|
||||
|
||||
/*Line (dependencies: -*/
|
||||
#define LV_USE_LINE 1
|
||||
|
@ -494,10 +605,22 @@ typedef void * lv_obj_user_data_t;
|
|||
#endif
|
||||
|
||||
/*Line meter (dependencies: *;)*/
|
||||
#define LV_USE_LMETER 1
|
||||
#define LV_USE_LINEMETER 1
|
||||
#if LV_USE_LINEMETER
|
||||
/* Draw line more precisely at cost of performance.
|
||||
* Useful if there are lot of lines any minor are visible
|
||||
* 0: No extra precision
|
||||
* 1: Some extra precision
|
||||
* 2: Best precision
|
||||
*/
|
||||
# define LV_LINEMETER_PRECISE 0
|
||||
#endif
|
||||
|
||||
/*Mask (dependencies: -)*/
|
||||
#define LV_USE_OBJMASK 1
|
||||
|
||||
/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
|
||||
#define LV_USE_MBOX 1
|
||||
#define LV_USE_MSGBOX 1
|
||||
|
||||
/*Page (dependencies: lv_cont)*/
|
||||
#define LV_USE_PAGE 1
|
||||
|
@ -507,11 +630,11 @@ typedef void * lv_obj_user_data_t;
|
|||
#endif
|
||||
|
||||
/*Preload (dependencies: lv_arc, lv_anim)*/
|
||||
#define LV_USE_PRELOAD 1
|
||||
#if LV_USE_PRELOAD != 0
|
||||
# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
|
||||
# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
|
||||
# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
|
||||
#define LV_USE_SPINNER 1
|
||||
#if LV_USE_SPINNER != 0
|
||||
# define LV_SPINNER_DEF_ARC_LENGTH 60 /*[deg]*/
|
||||
# define LV_SPINNER_DEF_SPIN_TIME 1000 /*[ms]*/
|
||||
# define LV_SPINNER_DEF_ANIM LV_SPINNER_TYPE_SPINNING_ARC
|
||||
#endif
|
||||
|
||||
/*Roller (dependencies: lv_ddlist)*/
|
||||
|
@ -531,13 +654,13 @@ typedef void * lv_obj_user_data_t;
|
|||
#define LV_USE_SPINBOX 1
|
||||
|
||||
/*Switch (dependencies: lv_slider)*/
|
||||
#define LV_USE_SW 1
|
||||
#define LV_USE_SWITCH 1
|
||||
|
||||
/*Text area (dependencies: lv_label, lv_page)*/
|
||||
#define LV_USE_TA 1
|
||||
#if LV_USE_TA != 0
|
||||
# define LV_TA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
|
||||
# define LV_TA_DEF_PWD_SHOW_TIME 1500 /*ms*/
|
||||
#define LV_USE_TEXTAREA 1
|
||||
#if LV_USE_TEXTAREA != 0
|
||||
# define LV_TEXTAREA_DEF_CURSOR_BLINK_TIME 400 /*ms*/
|
||||
# define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
|
||||
#endif
|
||||
|
||||
/*Table (dependencies: lv_label)*/
|
||||
|
@ -573,7 +696,6 @@ typedef void * lv_obj_user_data_t;
|
|||
|
||||
/*--END OF LV_CONF_H--*/
|
||||
|
||||
/*Be sure every define has a default value*/
|
||||
#include "lvgl/src/lv_conf_checker.h"
|
||||
|
||||
#endif /*LV_CONF_H*/
|
||||
|
||||
#endif /*End of "Content enable"*/
|
2
lvgl-sys/vendor/lvgl
vendored
2
lvgl-sys/vendor/lvgl
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 9eeaf61fa7ec2b5ea151374b135b85f15438938c
|
||||
Subproject commit 524709472757405ac0eef8d6d002632526430df3
|
|
@ -1,7 +1,7 @@
|
|||
[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)."
|
||||
version = "0.2.1"
|
||||
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 <crates.lvgl@caric.io>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/rafaelcaricio/lvgl-rs"
|
||||
|
@ -9,9 +9,50 @@ license = "MIT"
|
|||
readme = "../README.md"
|
||||
categories = ["api-bindings", "embedded", "gui", "no-std"]
|
||||
keywords = ["littlevgl", "lvgl", "graphical_interfaces"]
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
lvgl-sys = { path = "../lvgl-sys", version = "0.2.0" }
|
||||
lvgl-sys = { version = "0.5.2", path = "../lvgl-sys" }
|
||||
cty = "0.2.1"
|
||||
embedded-graphics = "0.6.2"
|
||||
cstr_core = { version = "0.2.0", default-features = false, features = ["alloc"] }
|
||||
embedded-graphics = "0.7.1"
|
||||
cstr_core = "0.2.3"
|
||||
bitflags = "1.2.1"
|
||||
|
||||
[features]
|
||||
alloc = ["cstr_core/alloc"]
|
||||
lvgl_alloc = ["alloc"]
|
||||
use-vendored-config = ["lvgl-sys/use-vendored-config"]
|
||||
|
||||
[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.3.0"
|
||||
heapless = "0.7.13"
|
||||
|
||||
[[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"]
|
||||
|
|
33
lvgl/build.rs
Normal file
33
lvgl/build.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use lvgl_codegen::{CodeGen, Rusty};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let rs = out_path.join("generated.rs");
|
||||
|
||||
let widgets_impl = lvgl_sys::_bindgen_raw_src();
|
||||
|
||||
let codegen = CodeGen::from(widgets_impl).unwrap();
|
||||
let widgets_impl: Vec<TokenStream> = codegen
|
||||
.get_widgets()
|
||||
.iter()
|
||||
.flat_map(|w| w.code(&()))
|
||||
.collect();
|
||||
|
||||
let code = quote! {
|
||||
#(#widgets_impl)*
|
||||
};
|
||||
|
||||
let mut file = File::create(rs).unwrap();
|
||||
writeln!(
|
||||
file,
|
||||
"/* automatically generated by lvgl-codegen */\n{}",
|
||||
code
|
||||
)
|
||||
.unwrap();
|
||||
}
|
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)
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
use crate::Color;
|
||||
use alloc::boxed::Box;
|
||||
use core::mem::MaybeUninit;
|
||||
use embedded_graphics;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::{drawable, DrawTarget};
|
||||
|
||||
pub struct DisplayDriver {
|
||||
pub(crate) raw: lvgl_sys::lv_disp_drv_t,
|
||||
}
|
||||
|
||||
impl DisplayDriver {
|
||||
// we should accept a Rc<RefCell<T>> and throw it in a box and add to the user_data of the callback handler function
|
||||
pub fn new<T, C>(device: &mut T) -> Self
|
||||
where
|
||||
T: DrawTarget<C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
let disp_drv = unsafe {
|
||||
// Create a display buffer for LittlevGL
|
||||
let mut display_buffer =
|
||||
Box::new(MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit().assume_init());
|
||||
|
||||
// Declare a buffer for the refresh rate
|
||||
const REFRESH_BUFFER_LEN: usize = 2;
|
||||
let refresh_buffer1 = Box::new(
|
||||
MaybeUninit::<
|
||||
[MaybeUninit<lvgl_sys::lv_color_t>;
|
||||
lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN],
|
||||
>::uninit()
|
||||
.assume_init(),
|
||||
);
|
||||
let refresh_buffer2 = Box::new(
|
||||
MaybeUninit::<
|
||||
[MaybeUninit<lvgl_sys::lv_color_t>;
|
||||
lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN],
|
||||
>::uninit()
|
||||
.assume_init(),
|
||||
);
|
||||
|
||||
// Initialize the display buffer
|
||||
lvgl_sys::lv_disp_buf_init(
|
||||
display_buffer.as_mut(),
|
||||
Box::into_raw(refresh_buffer1) as *mut cty::c_void,
|
||||
Box::into_raw(refresh_buffer2) as *mut cty::c_void,
|
||||
lvgl_sys::LV_HOR_RES_MAX * REFRESH_BUFFER_LEN as u32,
|
||||
);
|
||||
|
||||
// Descriptor of a display driver
|
||||
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit().assume_init();
|
||||
|
||||
// Basic initialization
|
||||
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
|
||||
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
|
||||
};
|
||||
Self { raw: disp_drv }
|
||||
}
|
||||
}
|
||||
|
||||
// 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!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// a reference is kept to the external drawing target (T)
|
||||
// the reference is kept in the callback function of the drawing handler
|
||||
// we need a reference counter for the drawing target and free the ref counter when the display is
|
||||
// destroyed.
|
||||
//type DrawHandler = Rc<RefCell<u8>>;
|
||||
//
|
||||
// impl Drop for DrawHandler {
|
||||
// fn drop(&mut self) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// }
|
||||
|
||||
unsafe extern "C" fn display_callback_wrapper<T, C>(
|
||||
disp_drv: *mut lvgl_sys::lv_disp_drv_t,
|
||||
area: *const lvgl_sys::lv_area_t,
|
||||
color_p: *mut lvgl_sys::lv_color_t,
|
||||
) where
|
||||
T: DrawTarget<C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
// We need to make sure panics can't escape across the FFI boundary.
|
||||
//let _ = std::panic::catch_unwind(|| {
|
||||
let display_driver = *disp_drv;
|
||||
|
||||
// Rust code closure reference
|
||||
let device = &mut *(display_driver.user_data as *mut T);
|
||||
|
||||
let ys = (*area).y1..=(*area).y2;
|
||||
let xs = ((*area).x1..=(*area).x2).enumerate();
|
||||
let x_len = ((*area).x2 - (*area).x1 + 1) as usize;
|
||||
let pixels = ys
|
||||
.enumerate()
|
||||
.map(|(iy, y)| {
|
||||
xs.clone().map(move |(ix, x)| {
|
||||
let color_len = x_len * iy + ix;
|
||||
let raw_color = Color::from_raw(*color_p.add(color_len));
|
||||
drawable::Pixel(Point::new(x as i32, y as i32), raw_color.into())
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
// TODO: Maybe find a way to use `draw_image` method on the device instance.
|
||||
let _ = device.draw_iter(pixels.into_iter());
|
||||
|
||||
// Indicate to LittlevGL that you are ready with the flushing
|
||||
lvgl_sys::lv_disp_flush_ready(disp_drv);
|
||||
//}); // end of panic::catch_unwind
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,68 @@
|
|||
#![no_std]
|
||||
//! [![github]](https://github.com/rafaelcaricio/lvgl-rs) [![crates-io]](https://crates.io/crates/lvgl) [![docs-rs]](crate)
|
||||
//!
|
||||
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
|
||||
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! [LVGL][1] bindings for Rust. A powerful and easy-to-use embedded GUI with many widgets, advanced visual effects, and
|
||||
//! low memory footprint. This crate is compatible with `#![no_std]` environments by default.
|
||||
//!
|
||||
//! [1]: https://docs.lvgl.io/v7/en/html/get-started/quick-overview.html
|
||||
//!
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
#[cfg(feature = "lvgl_alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
mod global;
|
||||
mod display;
|
||||
mod support;
|
||||
mod widgets;
|
||||
// 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;
|
||||
|
||||
pub use global::{UI, LvError};
|
||||
pub use display::DisplayDriver;
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5
lvgl/src/lv_core/mod.rs
Normal file
5
lvgl/src/lv_core/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#[macro_use]
|
||||
mod obj;
|
||||
pub mod style;
|
||||
|
||||
pub use obj::*;
|
216
lvgl/src/lv_core/obj.rs
Normal file
216
lvgl/src/lv_core/obj.rs
Normal file
|
@ -0,0 +1,216 @@
|
|||
use crate::lv_core::style::Style;
|
||||
use crate::Box;
|
||||
use crate::{Align, LvError, LvResult};
|
||||
use core::ptr;
|
||||
|
||||
/// Represents a native LVGL object
|
||||
pub trait NativeObject {
|
||||
/// Provide common way to access to the underlying native object pointer.
|
||||
fn raw(&self) -> LvResult<ptr::NonNull<lvgl_sys::lv_obj_t>>;
|
||||
}
|
||||
|
||||
/// Generic LVGL object.
|
||||
///
|
||||
/// This is the parent object of all widget types. It stores the native LVGL raw pointer.
|
||||
pub struct Obj {
|
||||
// We use a raw pointer here because we do not control this memory address, it is controlled
|
||||
// by LVGL's global state.
|
||||
raw: *mut lvgl_sys::lv_obj_t,
|
||||
}
|
||||
|
||||
impl NativeObject for Obj {
|
||||
fn raw(&self) -> LvResult<ptr::NonNull<lvgl_sys::lv_obj_t>> {
|
||||
if let Some(non_null_ptr) = ptr::NonNull::new(self.raw) {
|
||||
Ok(non_null_ptr)
|
||||
} else {
|
||||
Err(LvError::InvalidReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for all LVGL common operations on generic objects.
|
||||
pub trait Widget: NativeObject {
|
||||
type SpecialEvent;
|
||||
type Part: Into<u8>;
|
||||
|
||||
/// Construct an instance of the object from a raw pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// Provided the LVGL library can allocate memory this should be safe.
|
||||
///
|
||||
unsafe fn from_raw(raw_pointer: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self;
|
||||
|
||||
fn add_style(&self, part: Self::Part, style: Style) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_add_style(self.raw()?.as_mut(), part.into(), Box::into_raw(style.raw));
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pos(&mut self, x: i16, y: i16) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_pos(
|
||||
self.raw()?.as_mut(),
|
||||
x as lvgl_sys::lv_coord_t,
|
||||
y as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_size(&mut self, w: i16, h: i16) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_size(
|
||||
self.raw()?.as_mut(),
|
||||
w as lvgl_sys::lv_coord_t,
|
||||
h as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_width(&mut self, w: u32) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_width(self.raw()?.as_mut(), w as lvgl_sys::lv_coord_t);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_height(&mut self, h: u32) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_height(self.raw()?.as_mut(), h as lvgl_sys::lv_coord_t);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_align<C>(&mut self, base: &mut C, align: Align, x_mod: i32, y_mod: i32) -> LvResult<()>
|
||||
where
|
||||
C: NativeObject,
|
||||
{
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_align(
|
||||
self.raw()?.as_mut(),
|
||||
base.raw()?.as_mut(),
|
||||
align.into(),
|
||||
x_mod as lvgl_sys::lv_coord_t,
|
||||
y_mod as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Obj {
|
||||
type SpecialEvent = ();
|
||||
type Part = Part;
|
||||
|
||||
unsafe fn from_raw(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
|
||||
Self { raw: raw.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Obj {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
raw: unsafe { lvgl_sys::lv_obj_create(ptr::null_mut(), ptr::null_mut()) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_object {
|
||||
($item:ident) => {
|
||||
define_object!($item, event = (), part = $crate::Part);
|
||||
};
|
||||
($item:ident, event = $event_type:ty) => {
|
||||
define_object!($item, event = $event_type, part = $crate::Part);
|
||||
};
|
||||
($item:ident, part = $part_type:ty) => {
|
||||
define_object!($item, event = (), part = $part_type);
|
||||
};
|
||||
($item:ident, part = $part_type:ty, event = $event_type:ty) => {
|
||||
define_object!($item, event = $event_type, part = $part_type);
|
||||
};
|
||||
($item:ident, event = $event_type:ty, part = $part_type:ty) => {
|
||||
pub struct $item {
|
||||
core: $crate::Obj,
|
||||
}
|
||||
|
||||
unsafe impl Send for $item {}
|
||||
|
||||
impl $item {
|
||||
pub fn on_event<F>(&mut self, f: F) -> $crate::LvResult<()>
|
||||
where
|
||||
F: FnMut(Self, $crate::support::Event<<Self as $crate::Widget>::SpecialEvent>),
|
||||
{
|
||||
use $crate::NativeObject;
|
||||
unsafe {
|
||||
let mut raw = self.raw()?;
|
||||
let obj = raw.as_mut();
|
||||
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::<Self, F>),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::NativeObject for $item {
|
||||
fn raw(&self) -> $crate::LvResult<core::ptr::NonNull<lvgl_sys::lv_obj_t>> {
|
||||
self.core.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::Widget for $item {
|
||||
type SpecialEvent = $event_type;
|
||||
type Part = $part_type;
|
||||
|
||||
unsafe fn from_raw(raw_pointer: core::ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
|
||||
Self {
|
||||
core: $crate::Obj::from_raw(raw_pointer),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct State: u32 {
|
||||
/// Normal, released
|
||||
const DEFAULT = lvgl_sys::LV_STATE_DEFAULT as u32;
|
||||
/// Toggled or checked
|
||||
const CHECKED = lvgl_sys::LV_STATE_CHECKED as u32;
|
||||
/// Focused via keypad or encoder or clicked via touchpad/mouse
|
||||
const FOCUSED = lvgl_sys::LV_STATE_FOCUSED as u32;
|
||||
/// Edit by an encoder
|
||||
const EDITED = lvgl_sys::LV_STATE_EDITED as u32;
|
||||
/// Hovered by mouse (not supported now)
|
||||
const HOVERED = lvgl_sys::LV_STATE_HOVERED as u32;
|
||||
/// Pressed
|
||||
const PRESSED = lvgl_sys::LV_STATE_PRESSED as u32;
|
||||
/// Disabled or inactive
|
||||
const DISABLED = lvgl_sys::LV_STATE_DISABLED as u32;
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) fn get_bits(&self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Part {
|
||||
Main,
|
||||
All,
|
||||
}
|
||||
|
||||
impl From<Part> for u8 {
|
||||
fn from(self_: Part) -> u8 {
|
||||
match self_ {
|
||||
Part::Main => lvgl_sys::LV_OBJ_PART_MAIN as u8,
|
||||
Part::All => lvgl_sys::LV_OBJ_PART_ALL as u8,
|
||||
}
|
||||
}
|
||||
}
|
1207
lvgl/src/lv_core/style.rs
Normal file
1207
lvgl/src/lv_core/style.rs
Normal file
File diff suppressed because it is too large
Load diff
174
lvgl/src/mem.rs
Normal file
174
lvgl/src/mem.rs
Normal file
|
@ -0,0 +1,174 @@
|
|||
use core::mem;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
/// Places a sized `T` into LVGL memory.
|
||||
///
|
||||
/// This is useful for cases when we need to allocate memory on Rust side
|
||||
/// and handover the management of that memory to LVGL. May also be used in cases we
|
||||
/// want to use dynamic memory in the Rust side.
|
||||
pub(crate) struct Box<T>(NonNull<T>);
|
||||
|
||||
impl<T> Box<T> {
|
||||
/// Allocate memory using LVGL memory API and place `T` in the LVGL tracked memory.
|
||||
pub fn new(value: T) -> Box<T> {
|
||||
let size = mem::size_of::<T>();
|
||||
let inner = unsafe {
|
||||
let ptr = lvgl_sys::lv_mem_alloc(size as lvgl_sys::size_t) as *mut T;
|
||||
|
||||
// LVGL should align the memory address for us!
|
||||
assert_eq!(
|
||||
ptr as usize % mem::align_of::<T>(),
|
||||
0,
|
||||
"Memory address not aligned!"
|
||||
);
|
||||
|
||||
NonNull::new(ptr)
|
||||
.map(|p| {
|
||||
p.as_ptr().write(value);
|
||||
p
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Could not allocate memory {} bytes", size);
|
||||
})
|
||||
};
|
||||
Box(inner)
|
||||
}
|
||||
|
||||
pub fn into_raw(self) -> *mut T {
|
||||
let b = mem::ManuallyDrop::new(self);
|
||||
b.0.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Box<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_mem_free(self.0.as_ptr() as *const cty::c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Box<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Box<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for Box<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for Box<T> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { Self::new(self.0.as_ref().clone()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn place_value_in_lv_mem() {
|
||||
crate::lvgl_init();
|
||||
|
||||
let v = Box::new(5);
|
||||
drop(v);
|
||||
let v = Box::new(10);
|
||||
drop(v);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_complex_value_in_lv_mem() {
|
||||
crate::lvgl_init();
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: i8,
|
||||
t: i32,
|
||||
disp: i32,
|
||||
}
|
||||
|
||||
let initial_mem_info = mem_info();
|
||||
|
||||
let mut keep = Vec::new();
|
||||
for i in 0..100 {
|
||||
let p = Point {
|
||||
x: i,
|
||||
y: 42,
|
||||
t: 0,
|
||||
disp: -100,
|
||||
};
|
||||
|
||||
println!("{:?}", p);
|
||||
let mut b = Box::new(p);
|
||||
|
||||
println!("memory address is {:p}", b.as_mut());
|
||||
|
||||
let point = b.as_mut();
|
||||
if point.x != i {
|
||||
println!("{:?}", point);
|
||||
}
|
||||
assert_eq!(point.x, i);
|
||||
|
||||
let info = mem_info();
|
||||
println!("mem info: {:?}", &info);
|
||||
keep.push(b);
|
||||
}
|
||||
drop(keep);
|
||||
|
||||
unsafe {
|
||||
lvgl_sys::lv_mem_defrag();
|
||||
}
|
||||
|
||||
let final_info = mem_info();
|
||||
println!("mem info: {:?}", &final_info);
|
||||
|
||||
// If this fails, we are leaking memory! BOOM! \o/
|
||||
assert_eq!(initial_mem_info.free_size, final_info.free_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_object_in_lv_mem() {
|
||||
crate::lvgl_init();
|
||||
|
||||
let v1 = Box::new(5);
|
||||
let v2 = v1.clone();
|
||||
|
||||
// Ensure that the two objects have identical values.
|
||||
assert_eq!(*v1, *v2);
|
||||
// They should have different memory addresses, however.
|
||||
assert_ne!(v1.into_raw() as usize, v2.into_raw() as usize);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -1,267 +1,21 @@
|
|||
use alloc::boxed::Box;
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use cstr_core::CString;
|
||||
use crate::Widget;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::ptr::NonNull;
|
||||
use embedded_graphics::pixelcolor::{Rgb565, Rgb888};
|
||||
use lvgl_sys;
|
||||
|
||||
pub trait NativeObject {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t>;
|
||||
}
|
||||
pub type LvResult<T> = Result<T, LvError>;
|
||||
|
||||
pub struct ObjectX {
|
||||
raw: ptr::NonNull<lvgl_sys::lv_obj_t>,
|
||||
}
|
||||
|
||||
impl ObjectX {
|
||||
pub(crate) fn from_raw(raw: ptr::NonNull<lvgl_sys::lv_obj_t>) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeObject for ObjectX {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
|
||||
unsafe { ptr::NonNull::new_unchecked(self.raw.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Object: NativeObject {
|
||||
fn set_pos(&mut self, x: i16, y: i16) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_pos(
|
||||
self.raw().as_mut(),
|
||||
x as lvgl_sys::lv_coord_t,
|
||||
y as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, w: i16, h: i16) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_size(
|
||||
self.raw().as_mut(),
|
||||
w as lvgl_sys::lv_coord_t,
|
||||
h as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_width(&mut self, w: u32) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_width(self.raw().as_mut(), w as lvgl_sys::lv_coord_t);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_height(&mut self, h: u32) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_set_height(self.raw().as_mut(), h as lvgl_sys::lv_coord_t);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_align<C>(&mut self, base: &mut C, align: Align, x_mod: i32, y_mod: i32)
|
||||
where
|
||||
C: NativeObject,
|
||||
{
|
||||
let align = match align {
|
||||
Align::Center => lvgl_sys::LV_ALIGN_CENTER,
|
||||
Align::InTopLeft => lvgl_sys::LV_ALIGN_IN_TOP_LEFT,
|
||||
Align::InTopMid => lvgl_sys::LV_ALIGN_IN_TOP_MID,
|
||||
Align::InTopRight => lvgl_sys::LV_ALIGN_IN_TOP_RIGHT,
|
||||
Align::InBottomLeft => lvgl_sys::LV_ALIGN_IN_BOTTOM_LEFT,
|
||||
Align::InBottomMid => lvgl_sys::LV_ALIGN_IN_BOTTOM_MID,
|
||||
Align::InBottomRight => lvgl_sys::LV_ALIGN_IN_BOTTOM_RIGHT,
|
||||
Align::InLeftMid => lvgl_sys::LV_ALIGN_IN_LEFT_MID,
|
||||
Align::InRightMid => lvgl_sys::LV_ALIGN_IN_RIGHT_MID,
|
||||
Align::OutTopLeft => lvgl_sys::LV_ALIGN_OUT_TOP_LEFT,
|
||||
Align::OutTopMid => lvgl_sys::LV_ALIGN_OUT_TOP_MID,
|
||||
Align::OutTopRight => lvgl_sys::LV_ALIGN_OUT_TOP_RIGHT,
|
||||
Align::OutBottomLeft => lvgl_sys::LV_ALIGN_OUT_BOTTOM_LEFT,
|
||||
Align::OutBottomMid => lvgl_sys::LV_ALIGN_OUT_BOTTOM_MID,
|
||||
Align::OutBottomRight => lvgl_sys::LV_ALIGN_OUT_BOTTOM_RIGHT,
|
||||
Align::OutLeftTop => lvgl_sys::LV_ALIGN_OUT_LEFT_TOP,
|
||||
Align::OutLeftMid => lvgl_sys::LV_ALIGN_OUT_LEFT_MID,
|
||||
Align::OutLeftBottom => lvgl_sys::LV_ALIGN_OUT_LEFT_BOTTOM,
|
||||
Align::OutRightTop => lvgl_sys::LV_ALIGN_OUT_RIGHT_TOP,
|
||||
Align::OutRightMid => lvgl_sys::LV_ALIGN_OUT_RIGHT_MID,
|
||||
Align::OutRightBottom => lvgl_sys::LV_ALIGN_OUT_RIGHT_BOTTOM,
|
||||
} as lvgl_sys::lv_align_t;
|
||||
unsafe {
|
||||
lvgl_sys::lv_obj_align(
|
||||
self.raw().as_mut(),
|
||||
base.raw().as_mut(),
|
||||
align,
|
||||
x_mod as lvgl_sys::lv_coord_t,
|
||||
y_mod as lvgl_sys::lv_coord_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_style(&mut self, style: Style);
|
||||
}
|
||||
|
||||
impl Object for ObjectX {
|
||||
fn set_style(&mut self, style: Style) {
|
||||
unsafe {
|
||||
let boxed = Box::new(style.raw);
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), Box::into_raw(boxed));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_object {
|
||||
($item:ident) => {
|
||||
pub struct $item {
|
||||
core: ObjectX,
|
||||
}
|
||||
|
||||
impl NativeObject for $item {
|
||||
fn raw(&self) -> ptr::NonNull<lvgl_sys::lv_obj_t> {
|
||||
self.core.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for $item {
|
||||
fn set_style(&mut self, style: Style) {
|
||||
unsafe {
|
||||
let boxed = Box::new(style.raw);
|
||||
lvgl_sys::lv_obj_set_style(self.raw().as_mut(), Box::into_raw(boxed));
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_object!(Button);
|
||||
|
||||
impl Button {
|
||||
pub fn new<C>(parent: &mut C) -> Self
|
||||
where
|
||||
C: NativeObject,
|
||||
{
|
||||
let raw = unsafe {
|
||||
let ptr = lvgl_sys::lv_btn_create(parent.raw().as_mut(), ptr::null_mut());
|
||||
ptr::NonNull::new_unchecked(ptr)
|
||||
};
|
||||
let core = ObjectX::from_raw(raw);
|
||||
Self { core }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LabelAlign {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
Auto,
|
||||
}
|
||||
|
||||
define_object!(Label);
|
||||
|
||||
impl Label {
|
||||
pub fn new<C>(parent: &mut C) -> Self
|
||||
where
|
||||
C: NativeObject,
|
||||
{
|
||||
let raw = unsafe {
|
||||
let ptr = lvgl_sys::lv_label_create(parent.raw().as_mut(), ptr::null_mut());
|
||||
ptr::NonNull::new_unchecked(ptr)
|
||||
};
|
||||
let core = ObjectX::from_raw(raw);
|
||||
Self { core }
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
let text = CString::new(text).unwrap();
|
||||
unsafe {
|
||||
lvgl_sys::lv_label_set_text(self.core.raw().as_mut(), text.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_label_align(&mut self, align: LabelAlign) {
|
||||
let align = match align {
|
||||
LabelAlign::Left => lvgl_sys::LV_LABEL_ALIGN_LEFT,
|
||||
LabelAlign::Center => lvgl_sys::LV_LABEL_ALIGN_CENTER,
|
||||
LabelAlign::Right => lvgl_sys::LV_LABEL_ALIGN_RIGHT,
|
||||
LabelAlign::Auto => lvgl_sys::LV_LABEL_ALIGN_AUTO,
|
||||
} as lvgl_sys::lv_label_align_t;
|
||||
unsafe {
|
||||
lvgl_sys::lv_label_set_align(self.core.raw().as_mut(), align);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_recolor(&mut self, recolor: bool) {
|
||||
unsafe {
|
||||
lvgl_sys::lv_label_set_recolor(self.core.raw().as_mut(), recolor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Themes {
|
||||
Pretty,
|
||||
}
|
||||
|
||||
pub struct Style {
|
||||
raw: lvgl_sys::lv_style_t,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn new() -> Self {
|
||||
let raw = unsafe {
|
||||
let mut native_style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
||||
lvgl_sys::lv_style_copy(native_style.as_mut_ptr(), &lvgl_sys::lv_style_pretty);
|
||||
native_style.assume_init()
|
||||
};
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
/// Object's main background color.
|
||||
pub fn set_body_main_color(&mut self, color: Color) {
|
||||
self.raw.body.main_color = color.raw;
|
||||
}
|
||||
|
||||
/// Second color. If not equal to `set_body_main_color` a gradient will be drawn for the background.
|
||||
pub fn set_body_grad_color(&mut self, color: Color) {
|
||||
self.raw.body.grad_color = color.raw;
|
||||
}
|
||||
|
||||
/// Text color.
|
||||
pub fn set_text_color(&mut self, color: Color) {
|
||||
self.raw.text.color = color.raw;
|
||||
}
|
||||
|
||||
/// Font used for displaying the text.
|
||||
pub fn set_text_font(&mut self, font: &lvgl_sys::lv_font_t) {
|
||||
self.raw.text.font = font;
|
||||
}
|
||||
|
||||
/// Body radius for rounded corners.
|
||||
pub fn set_body_radius(&mut self, radius: i16) {
|
||||
self.raw.body.radius = radius;
|
||||
}
|
||||
|
||||
/// Border color.
|
||||
pub fn set_body_border_color(&mut self, color: Color) {
|
||||
self.raw.body.border.color = color.raw;
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Style {
|
||||
fn clone(&self) -> Self {
|
||||
let mut native_style = mem::MaybeUninit::<lvgl_sys::lv_style_t>::uninit();
|
||||
unsafe {
|
||||
lvgl_sys::lv_style_copy(
|
||||
native_style.as_mut_ptr(),
|
||||
&self.raw as *const lvgl_sys::lv_style_t,
|
||||
);
|
||||
Self {
|
||||
raw: native_style.assume_init(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub enum LvError {
|
||||
InvalidReference,
|
||||
Uninitialized,
|
||||
LvOOMemory,
|
||||
AlreadyInUse,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Color {
|
||||
raw: lvgl_sys::lv_color_t,
|
||||
pub(crate) raw: lvgl_sys::lv_color_t,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
@ -273,6 +27,18 @@ impl Color {
|
|||
pub fn from_raw(raw: lvgl_sys::lv_color_t) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
pub fn r(&self) -> u8 {
|
||||
unsafe { lvgl_sys::_LV_COLOR_GET_R(self.raw) as u8 }
|
||||
}
|
||||
|
||||
pub fn g(&self) -> u8 {
|
||||
unsafe { lvgl_sys::_LV_COLOR_GET_G(self.raw) as u8 }
|
||||
}
|
||||
|
||||
pub fn b(&self) -> u8 {
|
||||
unsafe { lvgl_sys::_LV_COLOR_GET_B(self.raw) as u8 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Rgb888 {
|
||||
|
@ -299,6 +65,120 @@ impl From<Color> for Rgb565 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Events are triggered in LVGL when something happens which might be interesting to
|
||||
/// the user, e.g. if an object:
|
||||
/// - is clicked
|
||||
/// - is dragged
|
||||
/// - its value has changed, etc.
|
||||
///
|
||||
/// All objects (such as Buttons/Labels/Sliders etc.) receive these generic events
|
||||
/// regardless of their type.
|
||||
pub enum Event<T> {
|
||||
/// The object has been pressed
|
||||
Pressed,
|
||||
|
||||
/// The object is being pressed (sent continuously while pressing)
|
||||
Pressing,
|
||||
|
||||
/// The input device is still being pressed but is no longer on the object
|
||||
PressLost,
|
||||
|
||||
/// Released before `long_press_time` config time. Not called if dragged.
|
||||
ShortClicked,
|
||||
|
||||
/// Called on release if not dragged (regardless to long press)
|
||||
Clicked,
|
||||
|
||||
/// Pressing for `long_press_time` config time. Not called if dragged.
|
||||
LongPressed,
|
||||
|
||||
/// Called after `long_press_time` config in every `long_press_rep_time` ms. Not
|
||||
/// called if dragged.
|
||||
LongPressedRepeat,
|
||||
|
||||
/// Called in every case when the object has been released even if it was dragged. Not called
|
||||
/// if slid from the object while pressing and released outside of the object. In this
|
||||
/// case, `Event<_>::PressLost` is sent.
|
||||
Released,
|
||||
|
||||
/// Pointer-like input devices events (E.g. mouse or touchpad)
|
||||
Pointer(PointerEvent),
|
||||
|
||||
/// Special event for the object type
|
||||
Special(T),
|
||||
}
|
||||
|
||||
impl<S> TryFrom<lvgl_sys::lv_event_t> for Event<S> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
const LV_EVENT_PRESSED: u32 = lvgl_sys::LV_EVENT_PRESSED as u32;
|
||||
const LV_EVENT_PRESSING: u32 = lvgl_sys::LV_EVENT_PRESSING as u32;
|
||||
const LV_EVENT_PRESS_LOST: u32 = lvgl_sys::LV_EVENT_PRESS_LOST as u32;
|
||||
const LV_EVENT_SHORT_CLICKED: u32 = lvgl_sys::LV_EVENT_SHORT_CLICKED as u32;
|
||||
const LV_EVENT_CLICKED: u32 = lvgl_sys::LV_EVENT_CLICKED as u32;
|
||||
const LV_EVENT_LONG_PRESSED: u32 = lvgl_sys::LV_EVENT_LONG_PRESSED as u32;
|
||||
const LV_EVENT_LONG_PRESSED_REPEAT: u32 = lvgl_sys::LV_EVENT_LONG_PRESSED_REPEAT as u32;
|
||||
const LV_EVENT_RELEASED: u32 = lvgl_sys::LV_EVENT_RELEASED as u32;
|
||||
|
||||
match value as u32 {
|
||||
LV_EVENT_PRESSED => Ok(Event::Pressed),
|
||||
LV_EVENT_PRESSING => Ok(Event::Pressing),
|
||||
LV_EVENT_PRESS_LOST => Ok(Event::PressLost),
|
||||
LV_EVENT_SHORT_CLICKED => Ok(Event::ShortClicked),
|
||||
LV_EVENT_CLICKED => Ok(Event::Clicked),
|
||||
LV_EVENT_LONG_PRESSED => Ok(Event::LongPressed),
|
||||
LV_EVENT_LONG_PRESSED_REPEAT => Ok(Event::LongPressedRepeat),
|
||||
LV_EVENT_RELEASED => Ok(Event::Released),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Event<S>> for lvgl_sys::lv_event_t {
|
||||
fn from(event: Event<S>) -> Self {
|
||||
let native_event = match event {
|
||||
Event::Pressed => lvgl_sys::LV_EVENT_PRESSED,
|
||||
Event::Pressing => lvgl_sys::LV_EVENT_PRESSING,
|
||||
Event::PressLost => lvgl_sys::LV_EVENT_PRESS_LOST,
|
||||
Event::ShortClicked => lvgl_sys::LV_EVENT_SHORT_CLICKED,
|
||||
Event::Clicked => lvgl_sys::LV_EVENT_CLICKED,
|
||||
Event::LongPressed => lvgl_sys::LV_EVENT_LONG_PRESSED,
|
||||
Event::LongPressedRepeat => lvgl_sys::LV_EVENT_LONG_PRESSED_REPEAT,
|
||||
Event::Released => lvgl_sys::LV_EVENT_RELEASED,
|
||||
// TODO: handle all types...
|
||||
_ => lvgl_sys::LV_EVENT_CLICKED,
|
||||
};
|
||||
native_event as lvgl_sys::lv_event_t
|
||||
}
|
||||
}
|
||||
|
||||
/// These events are sent only by pointer-like input devices (E.g. mouse or touchpad)
|
||||
pub enum PointerEvent {
|
||||
DragBegin,
|
||||
DragEnd,
|
||||
DragThrowBegin,
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn event_callback<T, F>(
|
||||
obj: *mut lvgl_sys::lv_obj_t,
|
||||
event: lvgl_sys::lv_event_t,
|
||||
) where
|
||||
T: Widget + Sized,
|
||||
F: FnMut(T, Event<T::SpecialEvent>),
|
||||
{
|
||||
// convert the lv_event_t to lvgl-rs Event type
|
||||
if let Ok(event) = event.try_into() {
|
||||
if let Some(obj_ptr) = NonNull::new(obj) {
|
||||
let object = T::from_raw(obj_ptr);
|
||||
// get the pointer from the Rust callback closure FnMut provided by users
|
||||
let user_closure = &mut *((*obj).user_data as *mut F);
|
||||
// call user callback closure
|
||||
user_closure(object, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Align {
|
||||
Center,
|
||||
InTopLeft,
|
||||
|
@ -322,3 +202,66 @@ pub enum Align {
|
|||
OutRightMid,
|
||||
OutRightBottom,
|
||||
}
|
||||
|
||||
impl From<Align> for u8 {
|
||||
fn from(self_: Align) -> u8 {
|
||||
let native = match self_ {
|
||||
Align::Center => lvgl_sys::LV_ALIGN_CENTER,
|
||||
Align::InTopLeft => lvgl_sys::LV_ALIGN_IN_TOP_LEFT,
|
||||
Align::InTopMid => lvgl_sys::LV_ALIGN_IN_TOP_MID,
|
||||
Align::InTopRight => lvgl_sys::LV_ALIGN_IN_TOP_RIGHT,
|
||||
Align::InBottomLeft => lvgl_sys::LV_ALIGN_IN_BOTTOM_LEFT,
|
||||
Align::InBottomMid => lvgl_sys::LV_ALIGN_IN_BOTTOM_MID,
|
||||
Align::InBottomRight => lvgl_sys::LV_ALIGN_IN_BOTTOM_RIGHT,
|
||||
Align::InLeftMid => lvgl_sys::LV_ALIGN_IN_LEFT_MID,
|
||||
Align::InRightMid => lvgl_sys::LV_ALIGN_IN_RIGHT_MID,
|
||||
Align::OutTopLeft => lvgl_sys::LV_ALIGN_OUT_TOP_LEFT,
|
||||
Align::OutTopMid => lvgl_sys::LV_ALIGN_OUT_TOP_MID,
|
||||
Align::OutTopRight => lvgl_sys::LV_ALIGN_OUT_TOP_RIGHT,
|
||||
Align::OutBottomLeft => lvgl_sys::LV_ALIGN_OUT_BOTTOM_LEFT,
|
||||
Align::OutBottomMid => lvgl_sys::LV_ALIGN_OUT_BOTTOM_MID,
|
||||
Align::OutBottomRight => lvgl_sys::LV_ALIGN_OUT_BOTTOM_RIGHT,
|
||||
Align::OutLeftTop => lvgl_sys::LV_ALIGN_OUT_LEFT_TOP,
|
||||
Align::OutLeftMid => lvgl_sys::LV_ALIGN_OUT_LEFT_MID,
|
||||
Align::OutLeftBottom => lvgl_sys::LV_ALIGN_OUT_LEFT_BOTTOM,
|
||||
Align::OutRightTop => lvgl_sys::LV_ALIGN_OUT_RIGHT_TOP,
|
||||
Align::OutRightMid => lvgl_sys::LV_ALIGN_OUT_RIGHT_MID,
|
||||
Align::OutRightBottom => lvgl_sys::LV_ALIGN_OUT_RIGHT_BOTTOM,
|
||||
};
|
||||
native as u8
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Animation {
|
||||
ON,
|
||||
OFF,
|
||||
}
|
||||
|
||||
impl From<Animation> for lvgl_sys::lv_anim_enable_t {
|
||||
fn from(anim: Animation) -> Self {
|
||||
match anim {
|
||||
Animation::ON => lvgl_sys::LV_ANIM_ON as u8,
|
||||
Animation::OFF => lvgl_sys::LV_ANIM_OFF as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn color_properties_accessible() {
|
||||
let color = Color::from_rgb((206, 51, 255));
|
||||
|
||||
if lvgl_sys::LV_COLOR_DEPTH == 32 {
|
||||
assert_eq!(color.r(), 206);
|
||||
assert_eq!(color.g(), 51);
|
||||
assert_eq!(color.b(), 255);
|
||||
} else if lvgl_sys::LV_COLOR_DEPTH == 16 {
|
||||
assert_eq!(color.r(), 25);
|
||||
assert_eq!(color.g(), 12);
|
||||
assert_eq!(color.b(), 31);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
200
lvgl/src/ui.rs
Normal file
200
lvgl/src/ui.rs
Normal file
|
@ -0,0 +1,200 @@
|
|||
use crate::Box;
|
||||
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr;
|
||||
use core::ptr::NonNull;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::time::Duration;
|
||||
use embedded_graphics::draw_target::DrawTarget;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::{pixelcolor::PixelColor, Pixel};
|
||||
|
||||
// There can only be a single reference to LVGL library.
|
||||
static LVGL_IN_USE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// TODO: Make this an external configuration
|
||||
const REFRESH_BUFFER_LEN: usize = 2;
|
||||
// Declare a buffer for the refresh rate
|
||||
pub(crate) const BUF_SIZE: usize = lvgl_sys::LV_HOR_RES_MAX as usize * REFRESH_BUFFER_LEN;
|
||||
|
||||
pub struct UI<T, C>
|
||||
where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
// 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<DisplayUserData<T, C>>,
|
||||
}
|
||||
|
||||
// LVGL does not use thread locals.
|
||||
unsafe impl<T, C> Send for UI<T, C>
|
||||
where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, C> UI<T, C>
|
||||
where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
pub fn init() -> LvResult<Self> {
|
||||
if LVGL_IN_USE
|
||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
crate::lvgl_init();
|
||||
Ok(Self {
|
||||
_not_sync: PhantomData,
|
||||
display_data: None,
|
||||
})
|
||||
} else {
|
||||
Err(LvError::AlreadyInUse)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disp_drv_register(&mut self, display: T) -> LvResult<()> {
|
||||
self.display_data = Some(DisplayUserData {
|
||||
display,
|
||||
phantom: PhantomData,
|
||||
});
|
||||
|
||||
let refresh_buffer1 = [Color::from_rgb((0, 0, 0)).raw; BUF_SIZE];
|
||||
let refresh_buffer2 = [Color::from_rgb((0, 0, 0)).raw; BUF_SIZE];
|
||||
|
||||
let mut disp_buf = MaybeUninit::<lvgl_sys::lv_disp_buf_t>::uninit();
|
||||
let mut disp_drv = MaybeUninit::<lvgl_sys::lv_disp_drv_t>::uninit();
|
||||
|
||||
unsafe {
|
||||
// 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,
|
||||
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());
|
||||
// Assign the buffer to the display
|
||||
disp_drv.buffer = Box::into_raw(Box::new(disp_buf.assume_init()));
|
||||
// Set your driver function
|
||||
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;
|
||||
// We need to remember to deallocate the `disp_drv` memory when dropping UI
|
||||
lvgl_sys::lv_disp_drv_register(Box::into_raw(disp_drv));
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_display_ref(&self) -> Option<&T> {
|
||||
match self.display_data.as_ref() {
|
||||
None => None,
|
||||
Some(v) => Some(&v.display),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scr_act(&self) -> LvResult<Obj> {
|
||||
unsafe {
|
||||
let screen = lvgl_sys::lv_disp_get_scr_act(ptr::null_mut());
|
||||
if let Some(v) = NonNull::new(screen) {
|
||||
Ok(Obj::from_raw(v))
|
||||
} else {
|
||||
Err(LvError::InvalidReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_send<W>(&mut self, obj: &mut W, event: Event<W::SpecialEvent>) -> LvResult<()>
|
||||
where
|
||||
W: Widget,
|
||||
{
|
||||
unsafe {
|
||||
lvgl_sys::lv_event_send(obj.raw()?.as_mut(), event.into(), ptr::null_mut());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DisplayUserData<T, C>
|
||||
where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
display: T,
|
||||
phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn display_callback_wrapper<T, C>(
|
||||
disp_drv: *mut lvgl_sys::lv_disp_drv_t,
|
||||
area: *const lvgl_sys::lv_area_t,
|
||||
color_p: *mut lvgl_sys::lv_color_t,
|
||||
) where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
// In the `std` world we would make sure to capture panics here and make them not escape across
|
||||
// the FFI boundary. Since this library is focused on embedded platforms, we don't
|
||||
// have an standard unwinding mechanism to rely upon.
|
||||
let display_driver = *disp_drv;
|
||||
// Rust code closure reference
|
||||
if !display_driver.user_data.is_null() {
|
||||
let user_data = &mut *(display_driver.user_data as *mut DisplayUserData<T, C>);
|
||||
let x1 = (*area).x1;
|
||||
let x2 = (*area).x2;
|
||||
let y1 = (*area).y1;
|
||||
let y2 = (*area).y2;
|
||||
// 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 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 LVGL to work and
|
||||
// what is the lvgl-rs wrapper responsibility.
|
||||
fn display_flush<T, C>(
|
||||
display: &mut T,
|
||||
(x1, x2): (i16, i16),
|
||||
(y1, y2): (i16, i16),
|
||||
color_p: *mut lvgl_sys::lv_color_t,
|
||||
) -> Result<(), T::Error>
|
||||
where
|
||||
T: DrawTarget<Color = C>,
|
||||
C: PixelColor + From<Color>,
|
||||
{
|
||||
let ys = y1..=y2;
|
||||
let xs = (x1..=x2).enumerate();
|
||||
let x_len = (x2 - x1 + 1) as usize;
|
||||
|
||||
// We use iterators here to ensure that the Rust compiler can apply all possible
|
||||
// optimizations at compile time.
|
||||
let pixels = ys.enumerate().flat_map(|(iy, y)| {
|
||||
xs.clone().map(move |(ix, x)| {
|
||||
let color_len = x_len * iy + ix;
|
||||
let lv_color = unsafe { *color_p.add(color_len) };
|
||||
let raw_color = Color::from_raw(lv_color);
|
||||
Pixel(Point::new(x as i32, y as i32), raw_color.into())
|
||||
})
|
||||
});
|
||||
|
||||
display.draw_iter(pixels)
|
||||
}
|
56
lvgl/src/widgets/arc.rs
Normal file
56
lvgl/src/widgets/arc.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::widgets::Arc;
|
||||
|
||||
impl Arc {
|
||||
// /// Set the start angle, for the given arc part.
|
||||
// /// 0 degrees for the right, 90 degrees for the bottom, etc.
|
||||
// pub fn set_start_angle(&mut self, angle: u16, part: ArcPart) -> LvResult<()> {
|
||||
// match part {
|
||||
// ArcPart::Background => unsafe {
|
||||
// lvgl_sys::lv_arc_set_bg_start_angle(self.core.raw()?.as_mut(), angle)
|
||||
// },
|
||||
// ArcPart::Indicator => unsafe {
|
||||
// lvgl_sys::lv_arc_set_start_angle(self.core.raw()?.as_mut(), angle)
|
||||
// },
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// /// Set the end angle, for the given arc part.
|
||||
// /// 0 degrees for the right, 90 degrees for the bottom, etc.
|
||||
// pub fn set_end_angle(&self, angle: u16, part: ArcPart) -> LvResult<()> {
|
||||
// match part {
|
||||
// ArcPart::Background => unsafe {
|
||||
// lvgl_sys::lv_arc_set_bg_end_angle(self.core.raw()?.as_mut(), angle)
|
||||
// },
|
||||
// ArcPart::Indicator => unsafe {
|
||||
// lvgl_sys::lv_arc_set_end_angle(self.core.raw()?.as_mut(), angle)
|
||||
// },
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// /// Rotate the arc, `angle` degrees clockwise.
|
||||
// pub fn set_rotation(&mut self, angle: u16) -> LvResult<()> {
|
||||
// unsafe {
|
||||
// lvgl_sys::lv_arc_set_rotation(self.core.raw()?.as_mut(), angle);
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
/// The different parts, of an arc object.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum ArcPart {
|
||||
/// The background of the arc.
|
||||
Background = lvgl_sys::LV_ARC_PART_BG as u8,
|
||||
/// The indicator of the arc.
|
||||
/// This is what moves/changes, depending on the arc's value.
|
||||
Indicator = lvgl_sys::LV_ARC_PART_INDIC as u8,
|
||||
}
|
||||
|
||||
impl From<ArcPart> for u8 {
|
||||
fn from(part: ArcPart) -> Self {
|
||||
part as u8
|
||||
}
|
||||
}
|
39
lvgl/src/widgets/bar.rs
Normal file
39
lvgl/src/widgets/bar.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use crate::support::Animation;
|
||||
use crate::widgets::Bar;
|
||||
use crate::{LvResult, NativeObject};
|
||||
|
||||
impl Bar {
|
||||
/// Set minimum and the maximum values of the bar
|
||||
pub fn set_range(&mut self, min: i16, max: i16) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_bar_set_range(self.core.raw()?.as_mut(), min, max);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a new value on the bar
|
||||
pub fn set_value(&mut self, value: i16, anim: Animation) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_bar_set_value(self.core.raw()?.as_mut(), value, anim.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The different parts, of a bar object.
|
||||
pub enum BarPart {
|
||||
/// The background of the bar.
|
||||
Background,
|
||||
/// The indicator of the bar.
|
||||
/// This is what moves/changes, depending on the bar's value.
|
||||
Indicator,
|
||||
}
|
||||
|
||||
impl From<BarPart> for u8 {
|
||||
fn from(component: BarPart) -> Self {
|
||||
match component {
|
||||
BarPart::Background => lvgl_sys::LV_BAR_PART_BG as u8,
|
||||
BarPart::Indicator => lvgl_sys::LV_BAR_PART_INDIC as u8,
|
||||
}
|
||||
}
|
||||
}
|
15
lvgl/src/widgets/gauge.rs
Normal file
15
lvgl/src/widgets/gauge.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
pub enum GaugePart {
|
||||
Main,
|
||||
Major,
|
||||
Needle,
|
||||
}
|
||||
|
||||
impl From<GaugePart> for u8 {
|
||||
fn from(part: GaugePart) -> Self {
|
||||
match part {
|
||||
GaugePart::Main => lvgl_sys::LV_GAUGE_PART_MAIN as u8,
|
||||
GaugePart::Major => lvgl_sys::LV_GAUGE_PART_MAJOR as u8,
|
||||
GaugePart::Needle => lvgl_sys::LV_GAUGE_PART_NEEDLE as u8,
|
||||
}
|
||||
}
|
||||
}
|
20
lvgl/src/widgets/label.rs
Normal file
20
lvgl/src/widgets/label.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use crate::widgets::Label;
|
||||
use crate::{LvResult, NativeObject};
|
||||
|
||||
impl Label {
|
||||
pub fn set_label_align(&mut self, align: LabelAlign) -> LvResult<()> {
|
||||
unsafe {
|
||||
lvgl_sys::lv_label_set_align(self.core.raw()?.as_mut(), align as u8);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum LabelAlign {
|
||||
Left = lvgl_sys::LV_LABEL_ALIGN_LEFT as u8,
|
||||
Center = lvgl_sys::LV_LABEL_ALIGN_CENTER as u8,
|
||||
Right = lvgl_sys::LV_LABEL_ALIGN_RIGHT as u8,
|
||||
Auto = lvgl_sys::LV_LABEL_ALIGN_AUTO as u8,
|
||||
}
|
12
lvgl/src/widgets/mod.rs
Normal file
12
lvgl/src/widgets/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
mod arc;
|
||||
mod bar;
|
||||
mod gauge;
|
||||
mod label;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use crate::NativeObject;
|
||||
pub use arc::*;
|
||||
pub use bar::*;
|
||||
pub use gauge::*;
|
||||
pub use label::*;
|
Loading…
Reference in a new issue