lv_bevy_ecs
Safe Rust bindings to the LVGL library using bevy_ecs. Compatible with #![no_std] environments by default. An alloc implementation is required.
[!NOTE] This crate is under heavy development and the API has not settled yet. Expect several breaking changes in every 0.x release.
What is an ECS?
ECS stands for Entity Component System. You can think of it as a database with rows (entities), columns (components) and jobs (systems).
You have to move LVGL objects into this database, so that they don't go out of scope and get deallocated. Bevy's Observers will mirror these database operations to LVGL's world.
But I don't want to use an ECS...
Enabling the no_ecs feature unlocks some functions that allow you to bring your own storage solution.
If you don't care about storage at all, and know in advance that a Widget will live for the rest of the program's execution,
you can call Widget::leak() to leak memory and prevent calling the destructor.
Check out no_ecs.rs on how to use these.
Usage
It is highly recommended to read Chapter 14 of the Unofficial Bevy Cheat Book before using this library.
- Create a project with
cargo neworesp-generate, then
-
This package depends on lightvgl-sys to generate the raw unsafe bindings. It needs an environment variable called
DEP_LV_CONFIG_PATHthat specifies the path to the folder containinglv_conf.hfile.It is recommended to add it inside
.cargo/config.toml
[]
= { = true, = "." }
- Assign a tick callback that measures elapsed time in milliseconds. This must be done before creating the UI. For other frameworks (like ESP-IDF or Embassy), you should use its tick counter instead to get precise and constant framerate.
# use *;
# use ;
#
lv_tick_set_cb;
-
Obtain a World instance with
LvglWorld::new();. This is a global variable, it can be stored in aLazyLockor passed around in anArc<Mutex<LvglWorld>>if needed elsewhere than in main().There are better but more complex patterns, like making LvglWorld local inside a specific task and using channels for communication. Alternatively, making the state global and synchronizing with LVGL before
lv_timer_handler(). This way Mutex usage can be minimized.
# use LvglWorld;
# use ;
static WORLD: = new;
- Last thing is to call
lv_timer_handler()periodically.
# use *;
# use sleep;
# use ;
#
loop
Check the documentation and the examples for further usage.
Running the demo
On Windows, you have to install SDL2.
Building for embedded
Example projects are available targeting ESP32 and ESP32-P4 with std enabled: lvgl-bevy-demo, lvgl-bevy-demo-dsi
Heap Allocation
lvgl-alloc feature
If you don't have an allocator, a GlobalAlloc for Rust leveraging the LVGL memory allocator is provided, but not enabled by default.
Can be enabled with the feature lvgl-alloc. This will make all dynamic memory to be allocated by LVGL internal memory manager.
rust-alloc feature
If you already have an allocator, you can enable the rust-alloc feature to forward the LVGL memory allocator functions to the Rust alloc crate.
This needs LV_USE_STDLIB_MALLOC set to LV_STDLIB_CUSTOM in lv_conf.h.
Additionally, an optional implementation of the get_memory_stats(&mut lv_mem_monitor_t) function can be provided.
Check the examples and sample projects for reference implementation.
Minimizing binary size
In order to remove even more unused functions, the Cross-language Link-Time Optimization functionality of LLVM can be enabled. Unfortunately, this is not available on every platform, especially on those that use gcc as the linker.
Make sure to match your clang version with your rustc version.
.cargo/config.toml
[]
= ["-C", "linker-plugin-lto", "-C", "link-arg=-fuse-ld=lld"]
= "clang-21"
[]
= "-flto=full"
= "clang-21"
Cargo.toml
[]
= "z"
= "fat"
= 1
Features
- Displays
- Widgets
- Events
- Styles
- Input devices
- Animations
- Timers, lv_async_call
- Subjects
- Logging
- LVGL allocator
- "no_ecs" mode
- #![no_std] compatibility
- LVGL docstrings
- Cross-language LTO
- Defmt support
- OOP-style function calls
- Auto-generated enums
- File system
- Custom fonts
- Snapshots
- Non-widget functions
- Layouts
- XML UI
Compatibility table
| lv_bevy_ecs | bevy_ecs | lightvgl-sys |
|---|---|---|
| 0.10.x | 0.18.x | 9.5.x |
| 0.9.x | 0.18.x | 9.5.x |
| 0.8.x | 0.18.x | 9.5.x |
| 0.7.x | 0.18.x | 9.4.x |
| 0.6.x | 0.17.x | 9.4.x |
| 0.5.x | 0.17.x | 9.4.x |
| 0.4.x | 0.17.x | 9.3.x |
| 0.3.x | 0.16.x | 9.3.x |
| 0.2.x | 0.16.x | 9.2.x |
Contributing
Feel free to open issues for features you find important and missing. I am not completely satisfied with the API, so open to API improvement ideas as well.
Troubleshooting
Unable to generate bindings: fatal error: 'inttypes.h' file not found
Try adding this environment variable to .cargo/config.toml:
= "-I/usr/include"
Thanks
This project heavily builds upon the work in the the original lv_binding_rust repo.