Compare commits

...

106 commits

Author SHA1 Message Date
Jay Jackson 601a87d513 add use-vendored-config feature 2022-05-19 14:23:44 +02:00
Jay Jackson faa3a19215 bump versions 2022-05-17 22:52:57 +02:00
Jay Jackson 029d230cab remove leftover files 2022-05-17 22:52:57 +02:00
Jay Jackson ade4ae5a2d fix clippy warnings 2022-05-17 22:52:57 +02:00
Jay Jackson 37950d6e2e windows support 2022-05-17 22:52:57 +02:00
Sam Jones 763b78e45d Implement Clone for Style 2022-03-22 15:17:17 +01:00
Rafael Caricio 0a5b5a0416
🇺🇦 This project supports Ukraine 2022-03-08 11:52:01 +00:00
Rafael Caricio a5c27b58ae
🇺🇦 This project supports Ukraine 2022-03-08 11:49:26 +00:00
Tim Newsome cbbdc7e750 Add --features="alloc" to run demo
Without that, I got the following error from cargo:
```
error: target `demo` in package `lvgl` requires the features: `alloc`
Consider enabling them by passing, e.g., `--features="alloc"`
```
2022-01-10 14:35:54 +01:00
Rafael Caricio 56cb470dc8 Remove lazy_static dependency 2021-05-26 20:55:38 +02:00
Rafael Caricio 043bb52881 Add LVGL Rust Global Allocator feature 2021-05-26 20:55:38 +02:00
Rafael Caricio a5ffa62013 lv_mem_monitor_t is not allocated by lv_mem_monitor function, we need to pass pointer to a valid memory address 2021-05-24 23:17:33 +02:00
Rafael Caricio 348963b80d Upgrade to lvgl v7.10.1
- Clean up demo
- Remove warning from rustc
- Improve test of custom Box implementation
- Upgrade dep versions
2021-05-24 23:17:33 +02:00
Sam Jones 3791b211b8 Fix lvgl-sys not respecting DEP_LV_CONFIG_PATH 2021-05-19 14:24:01 +02:00
Rafael Caricio d699101afe Format code 2021-03-07 18:32:27 +01:00
Rafael Caricio f60d8d999b Upgrade LVGL vendored to v7.10.1 2021-03-07 18:32:27 +01:00
Rafael Caricio ab8b8ae05f
Remove usage of deprecated method 2021-03-07 18:03:45 +01:00
Rafael Caricio 9b9ea48920 Add some initial formating to docs page 2021-03-07 16:34:11 +01:00
Rafael Caricio 7f5bec7366 Release 0.5.2 2021-03-07 00:34:19 +01:00
Rafael Caricio 3f226e94a4 Release 0.5.1 2021-03-06 23:39:31 +01:00
Rafael Caricio bcd771df47 Disable lvgl original comments in bindings
We disable the comments generation here because some comments
cause the `cargo test` command to fail. Some of the original
comments have invalid tokens like `TYPEDEFS` causing issues.
We should recommend users to look the original comments in the
original lvgl in their C API docs.
2021-03-06 21:50:39 +01:00
Rafael Caricio da8bae1f55 Fix build in Docs.rs 2021-03-06 21:50:39 +01:00
Jai A d3a2a9eff3 Fix compile error 2021-02-15 18:58:36 +01:00
Ralf Weinbrecher 65b68e0f7c Update README: Added a hint to install sdl2 before running the demos on macOS 2020-11-17 19:48:56 +01:00
Rafael Caricio 81cb1bee4c Compile to WASM using emscripten 2020-09-22 20:54:47 +02:00
Rafael Caricio 8fe36f6102 Expose RGB values from Color 2020-08-27 11:40:08 +02:00
Rafael Caricio d442ab99cc Release 0.4.0 2020-06-19 18:55:30 +02:00
Rafael Caricio f4618fd635 Simplify examples, don't use threads 2020-06-19 15:48:44 +02:00
Rafael Caricio 385fc6b764 Remove threads from bar example 2020-06-19 15:48:44 +02:00
Rafael Caricio 0761e36c76 Run apt-get update in CI 2020-06-19 15:48:44 +02:00
Rafael Caricio ef94e5d5d4 Install SDL2 2020-06-19 15:48:44 +02:00
Rafael Caricio a1aec89456 Checkout submodules in GH actions 2020-06-19 15:48:44 +02:00
Rafael Caricio 98ae8e0ced Update readme to remove alloc reference 2020-06-19 15:48:44 +02:00
Rafael Caricio 52fafecdde Remove unused dependencies 2020-06-19 15:48:44 +02:00
Rafael Caricio 64527c3f2a Fix examples 2020-06-19 15:48:44 +02:00
Rafael Caricio adee5994dc Simplify code 2020-06-19 15:48:44 +02:00
Rafael Caricio 60c3fc7ed7 Fix comment misconception 2020-06-19 15:48:44 +02:00
Rafael Caricio 6a9bd2d87f Fix UB, the value in MaybeUninit was being dropped 2020-06-19 15:48:44 +02:00
Rafael Caricio e3fd8d5736 LVGL handles zero sized 2020-06-19 15:48:44 +02:00
Rafael Caricio 3e7e056da6 Some additional checks on using LVGL heap memory allocator 2020-06-19 15:48:44 +02:00
Rafael Caricio e70ba3a752 Incorporate clippy suggestions 2020-06-19 15:48:44 +02:00
Rafael Caricio 38d7a0e974 Clean up 2020-06-19 15:48:44 +02:00
Rafael Caricio 46562f23cf Revert display buffer allocation to use LVGL backed Box 2020-06-19 15:48:44 +02:00
Rafael Caricio 8b2879f93b Remove alloc dependency 2020-06-19 15:48:44 +02:00
Rafael Caricio 657640d206 First step in removing dependency on alloc crate 2020-06-19 15:48:44 +02:00
Rafael Caricio b85226aada
Update rust.yml 2020-06-15 01:53:42 +02:00
Rafael Caricio 6ddb606855
Update rust.yml 2020-06-15 01:49:33 +02:00
Rafael Caricio 931dead6ab
Initial CI setup. 2020-06-15 01:45:26 +02:00
Rafael Caricio 5b48d5faad Release 0.3.3 2020-06-14 19:46:50 +02:00
Rafael Caricio 78f0dbcefa Release 0.3.2 2020-06-14 17:02:07 +02:00
Rafael Caricio 3c8f892c19 Fix examples 2020-06-14 16:16:59 +02:00
Rafael Caricio 655300d3e9 Release 0.3.1 2020-06-14 14:49:02 +02:00
Rafael Caricio a6bb2e4012 Release 0.3.1 2020-06-14 14:42:05 +02:00
Rafael Caricio b8dfd896c2 Use a Rust implementation of string.x lib 2020-06-14 14:41:23 +02:00
Rafael Caricio fd65ecbe34 Remove explicitly listing files 2020-06-14 11:27:13 +02:00
Rafael Caricio 2277334491 Explicity define build script 2020-06-14 11:25:22 +02:00
Rafael Caricio 78841f1538 Update metadata 2020-06-14 11:18:33 +02:00
Rafael Caricio ad5a3deea4 Fix types 2020-06-14 11:10:29 +02:00
Rafael Caricio ceabc135cc No need to generate lvgl-codegen as CLI tool 2020-06-14 11:10:29 +02:00
Rafael Caricio 74ae3a8b5b Use bindgen output to generate code 2020-06-14 11:10:29 +02:00
Rafael Caricio dbb1d4a8d1 Fix clang dependency 2020-06-13 21:50:11 +02:00
Rafael Caricio cd3397b254 Remove unused dependencies 2020-06-13 20:31:24 +02:00
Rafael Caricio 0e256957ba Fix UB 2020-06-13 20:31:24 +02:00
Rafael Caricio 67cbca4942 Generate methods with str as argument 2020-06-13 20:31:24 +02:00
Rafael Caricio b0113fc747 Update readme 2020-06-13 20:31:24 +02:00
Rafael Caricio de9b6512a7 Generate all widgets 2020-06-13 20:31:24 +02:00
Rafael Caricio d970deaa9d Use lvgl-codegen 2020-06-13 20:31:24 +02:00
Rafael Caricio f0ec94cbf9 Use generated widgets 2020-06-13 20:31:24 +02:00
Rafael Caricio 6f5b771b7d Add missing C func to link 2020-06-13 20:31:24 +02:00
Rafael Caricio 55f206e2cd Fix allocation 2020-06-13 20:31:24 +02:00
Rafael Caricio 311c8c6cc9 Last version with clang dep 2020-06-13 20:31:24 +02:00
Rafael Caricio 119d44ef91 Generate methods for Widgets 2020-06-13 20:31:24 +02:00
Rafael Caricio 335261ed6e Fix examples 2020-06-07 20:50:45 +02:00
Rafael Caricio 36bae837f8 Let users deal with Errors 2020-06-07 20:50:45 +02:00
Jakub Clark 80d976ac1f Remove unused directory 2020-06-06 09:27:53 +02:00
Jakub Clark 546781f1f6 Add arc widget 2020-06-06 09:27:53 +02:00
Rafael Caricio 25efb6da91 Simplify object creation 2020-06-05 23:22:21 +02:00
Rafael Caricio 88d7d426ca Keep consistent naming with LVGL 2020-06-05 22:47:13 +02:00
Rafael Caricio 469fdf2508 Remove code duplication 2020-06-05 22:36:15 +02:00
Rafael Caricio 5015711141 Add Gauge widget 2020-06-04 23:09:54 +02:00
Rafael Caricio 66d98430d8 Use Opacity type 2020-06-04 21:29:21 +02:00
Rafael Caricio 3788b4c66b Fix examples 2020-06-04 20:47:40 +02:00
Rafael Caricio 461da8814c Move object related types to its own module 2020-06-04 20:47:40 +02:00
Rafael Caricio e8757f74de Move align conversion to Into<u8> trait impl 2020-06-04 20:47:40 +02:00
Rafael Caricio 52e31496c6 Update Bar example 2020-06-04 20:47:40 +02:00
Rafael Caricio c494d8fac0 Generate code for styles properties 2020-06-04 20:47:40 +02:00
Rafael Caricio 93a7fd4316 Enable use by lvgl-rs of v7.0.1 of LVGL 2020-06-04 20:47:40 +02:00
Rafael Caricio 59c567ae1c LVGL 7.0.1 basic setup 2020-06-04 20:47:40 +02:00
Rafael Caricio 460a3d1d30 Remove old repo link 2020-06-04 20:47:40 +02:00
Rafael Caricio 63e33d052f Remove some UB 2020-06-02 19:59:41 +02:00
Rafael Caricio 11695e20c1 Rename ObjectX to GenericObject 2020-06-02 19:21:44 +02:00
Rafael Caricio 9ec78a70cb Add info about what is implemented to the README 2020-06-02 09:39:02 +02:00
Rafael Caricio 532cf3cf1b Support more styling options 2020-06-01 22:45:33 +02:00
Rafael Caricio 3faec0e2b1 Panics if C code freed the raw widget pointer 2020-06-01 19:54:35 +02:00
Kuba Clark fcdf803ab7
Possible to set style for bar components (#14) 2020-05-31 20:52:05 +02:00
Rafael Caricio b8a559a6cf Make it possible to send events 2020-05-31 19:24:45 +02:00
Rafael Caricio 096d175fc0 More welcoming wording in the README 2020-05-31 18:53:09 +02:00
harksin d22c419316
📝 improve demo documentation (#13) 2020-05-31 18:51:12 +02:00
Rafael Caricio 0349f1c77a Make it possible to define event callbacks 2020-05-31 18:48:17 +02:00
Rafael Caricio 2dd59d212d Clarify the display flush procedure 2020-05-31 13:28:02 +02:00
Rafael Caricio a72622e1c8 Set style can be part of the trait definition 2020-05-31 12:39:47 +02:00
Rafael Caricio b132edc6fc Move widgets to designated modules 2020-05-31 12:22:18 +02:00
Rafael Caricio cb8a78137d Possible to set values in the bar widget with animations 2020-05-31 12:07:24 +02:00
Rafael Caricio e0e3924789 Small improvements in bar example 2020-05-31 09:30:26 +02:00
Kuba Clark 2fae95bdfb
Bar widget (#12)
* Implement simple bar widget

* Make it easier to create new examples

* Add bar example

* Add README to examples

* Remove unnecessary imports
2020-05-31 09:12:37 +02:00
Rafael Caricio 81d924e854
Create CODE_OF_CONDUCT.md 2020-05-31 00:13:57 +02:00
47 changed files with 4784 additions and 801 deletions

33
.github/workflows/rust.yml vendored Normal file
View 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
View file

@ -12,4 +12,3 @@ Cargo.lock
examples/demo/target/
lvgl-sys/target/
lvgl/target/
lvgl-sys/src/bindings.rs

2
.gitmodules vendored
View file

@ -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
View 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

View file

@ -1,6 +1,6 @@
[workspace]
members = [
"lvgl",
"lvgl-codegen",
"lvgl-sys",
"examples/demo",
]

View file

@ -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> &middot;
<a href="https://github.com/rafaelcaricio/lvgl-rs-wasm">Rust to WASM demo</a> &middot;
<a href="https://lvgl.io/">Official LVGL Website </a> &middot;
<a href="https://github.com/littlevgl/lvgl">C library repository</a> &middot;
<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
View 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
View 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
View 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
View 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
View 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;
}

View file

@ -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"

View file

@ -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
```

View file

@ -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 { &noto_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
View 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
View 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
View 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
View 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
View 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());
}
}

View file

@ -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 = []

View file

@ -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

View file

@ -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))
}

View file

@ -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
View 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()
}

View file

@ -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;
}

View file

@ -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"*/

@ -1 +1 @@
Subproject commit 9eeaf61fa7ec2b5ea151374b135b85f15438938c
Subproject commit 524709472757405ac0eef8d6d002632526430df3

View file

@ -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
View 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
View 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)
}
}

View file

@ -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
}

View file

@ -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();
}
}
}

View file

@ -1,12 +1,68 @@
#![no_std]
//! [![github]](https://github.com/rafaelcaricio/lvgl-rs)&ensp;[![crates-io]](https://crates.io/crates/lvgl)&ensp;[![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
View file

@ -0,0 +1,5 @@
#[macro_use]
mod obj;
pub mod style;
pub use obj::*;

216
lvgl/src/lv_core/obj.rs Normal file
View 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

File diff suppressed because it is too large Load diff

174
lvgl/src/mem.rs Normal file
View 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
}
}

View file

@ -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
View 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)
}

View file

56
lvgl/src/widgets/arc.rs Normal file
View 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
View 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
View 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
View 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
View 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::*;