Touch input device #27
18 changed files with 391 additions and 52 deletions
|
@ -47,6 +47,12 @@ $ DEP_LV_CONFIG_PATH=`pwd` cargo build -Zfeatures=build_dep
|
||||||
|
|
||||||
## Running the demo
|
## Running the demo
|
||||||
|
|
||||||
|
**Hint for macOS users**: Before you run the demos you need to make sure you have [libsdl](https://www.libsdl.org) installed on your machine. To install it, use HomeBrew:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ brew install sdl2
|
||||||
|
```
|
||||||
|
|
||||||
[This project contains examples that can run in a desktop simulator.](./examples)
|
[This project contains examples that can run in a desktop simulator.](./examples)
|
||||||
|
|
||||||
First, make sure to pull `lvgl-rs` submodules:
|
First, make sure to pull `lvgl-rs` submodules:
|
||||||
|
|
|
@ -13,6 +13,10 @@ embedded-graphics-simulator = "0.2.0"
|
||||||
heapless = "0.5.5"
|
heapless = "0.5.5"
|
||||||
cstr_core = { version = "0.2.0", features = ["alloc"] }
|
cstr_core = { version = "0.2.0", features = ["alloc"] }
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "simple"
|
||||||
|
path = "simple.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "demo"
|
name = "demo"
|
||||||
path = "demo.rs"
|
path = "demo.rs"
|
||||||
|
|
|
@ -8,14 +8,11 @@ use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Arc, Label, LabelAlign};
|
use lvgl::widgets::{Arc, Label, LabelAlign};
|
||||||
use lvgl::{self, Align, Color, Part, State, UI};
|
use lvgl::{self, Align, Color, Part, State, UI};
|
||||||
use lvgl::{LvError, Widget};
|
use lvgl::{LvError, Widget};
|
||||||
use lvgl_sys;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
|
||||||
));
|
|
||||||
|
|
||||||
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
let mut window = Window::new("Arc Example", &output_settings);
|
let mut window = Window::new("Arc Example", &output_settings);
|
||||||
|
|
|
@ -7,14 +7,11 @@ use embedded_graphics_simulator::{
|
||||||
use lvgl::style::Style;
|
use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Bar, Label, LabelAlign};
|
use lvgl::widgets::{Bar, Label, LabelAlign};
|
||||||
use lvgl::{self, Align, Animation, Color, Event, LvError, Part, State, Widget, UI};
|
use lvgl::{self, Align, Animation, Color, Event, LvError, Part, State, Widget, UI};
|
||||||
use lvgl_sys;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
|
||||||
));
|
|
||||||
|
|
||||||
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
let mut window = Window::new("Bar Example", &output_settings);
|
let mut window = Window::new("Bar Example", &output_settings);
|
||||||
|
|
|
@ -4,26 +4,34 @@ use embedded_graphics::prelude::*;
|
||||||
use embedded_graphics_simulator::{
|
use embedded_graphics_simulator::{
|
||||||
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use lvgl::input_device::{BufferStatus, InputData, Pointer};
|
||||||
use lvgl::style::Style;
|
use lvgl::style::Style;
|
||||||
use lvgl::widgets::{Btn, Label};
|
use lvgl::widgets::{Btn, Label};
|
||||||
use lvgl::{self, Align, Color, Event, LvError, Part, State, Widget, UI};
|
use lvgl::{self, Align, Color, LvError, Part, State, Widget, UI};
|
||||||
use lvgl_sys;
|
|
||||||
use std::time::Instant;
|
use std::thread::sleep;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
|
||||||
));
|
|
||||||
|
|
||||||
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
let mut window = Window::new("Bar Example", &output_settings);
|
let mut window = Window::new("Bar Example", &output_settings);
|
||||||
|
|
||||||
let mut ui = UI::init()?;
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
// Implement and register your display:
|
// Register your display:
|
||||||
ui.disp_drv_register(display)?;
|
ui.disp_drv_register(display)?;
|
||||||
|
|
||||||
|
// Define the initial state of your input
|
||||||
|
let mut latest_touch_status = InputData::Touch(Point::new(0, 0)).released().once();
|
||||||
|
|
||||||
|
// Register a new input device that's capable of reading the current state of the input
|
||||||
|
let mut touch_screen = Pointer::new(|| latest_touch_status);
|
||||||
|
ui.indev_drv_register(&mut touch_screen)?;
|
||||||
|
|
||||||
// Create screen and widgets
|
// Create screen and widgets
|
||||||
let mut screen = ui.scr_act()?;
|
let mut screen = ui.scr_act()?;
|
||||||
|
|
||||||
|
@ -40,6 +48,7 @@ fn main() -> Result<(), LvError> {
|
||||||
|
|
||||||
let mut btn_state = false;
|
let mut btn_state = false;
|
||||||
button.on_event(|mut btn, event| {
|
button.on_event(|mut btn, event| {
|
||||||
|
println!("Button received event: {:?}", event);
|
||||||
if let lvgl::Event::Clicked = event {
|
if let lvgl::Event::Clicked = event {
|
||||||
if btn_state {
|
if btn_state {
|
||||||
let nt = CString::new("Click me!").unwrap();
|
let nt = CString::new("Click me!").unwrap();
|
||||||
|
@ -49,17 +58,25 @@ fn main() -> Result<(), LvError> {
|
||||||
btn_lbl.set_text(nt.as_c_str()).unwrap();
|
btn_lbl.set_text(nt.as_c_str()).unwrap();
|
||||||
}
|
}
|
||||||
btn_state = !btn_state;
|
btn_state = !btn_state;
|
||||||
println!("Clicked! Inner..");
|
|
||||||
btn.toggle().unwrap();
|
btn.toggle().unwrap();
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut loop_started = Instant::now();
|
let mut loop_started = Instant::now();
|
||||||
|
let mut latest_touch_point = Point::new(0, 0);
|
||||||
'running: loop {
|
'running: loop {
|
||||||
ui.task_handler();
|
ui.task_handler();
|
||||||
window.update(ui.get_display_ref().unwrap());
|
window.update(ui.get_display_ref().unwrap());
|
||||||
|
|
||||||
for event in window.events() {
|
let mut events = window.events().peekable();
|
||||||
|
|
||||||
|
if events.peek().is_none() {
|
||||||
|
latest_touch_status = InputData::Touch(latest_touch_point.clone())
|
||||||
|
.released()
|
||||||
|
.once();
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
SimulatorEvent::MouseButtonUp {
|
SimulatorEvent::MouseButtonUp {
|
||||||
mouse_btn: _,
|
mouse_btn: _,
|
||||||
|
@ -67,13 +84,16 @@ fn main() -> Result<(), LvError> {
|
||||||
} => {
|
} => {
|
||||||
println!("Clicked on: {:?}", point);
|
println!("Clicked on: {:?}", point);
|
||||||
// Send a event to the button directly
|
// Send a event to the button directly
|
||||||
ui.event_send(&mut button, Event::Clicked)?;
|
latest_touch_point = point.clone();
|
||||||
|
latest_touch_status = InputData::Touch(point).pressed().once();
|
||||||
}
|
}
|
||||||
SimulatorEvent::Quit => break 'running,
|
SimulatorEvent::Quit => break 'running,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(15));
|
||||||
|
|
||||||
ui.tick_inc(loop_started.elapsed());
|
ui.tick_inc(loop_started.elapsed());
|
||||||
loop_started = Instant::now();
|
loop_started = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,8 @@ use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
|
||||||
));
|
|
||||||
|
|
||||||
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
let mut window = Window::new("PineTime", &output_settings);
|
let mut window = Window::new("PineTime", &output_settings);
|
||||||
|
|
|
@ -6,14 +6,11 @@ use embedded_graphics_simulator::{
|
||||||
use lvgl::style::{Opacity, Style};
|
use lvgl::style::{Opacity, Style};
|
||||||
use lvgl::widgets::Gauge;
|
use lvgl::widgets::Gauge;
|
||||||
use lvgl::{self, Align, Color, LvError, Part, State, Widget, UI};
|
use lvgl::{self, Align, Color, LvError, Part, State, Widget, UI};
|
||||||
use lvgl_sys;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
fn main() -> Result<(), LvError> {
|
fn main() -> Result<(), LvError> {
|
||||||
let display: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
lvgl_sys::LV_HOR_RES_MAX,
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
lvgl_sys::LV_VER_RES_MAX,
|
|
||||||
));
|
|
||||||
|
|
||||||
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
let mut window = Window::new("Gauge Example", &output_settings);
|
let mut window = Window::new("Gauge Example", &output_settings);
|
||||||
|
@ -36,7 +33,7 @@ fn main() -> Result<(), LvError> {
|
||||||
gauge_style.set_radius(State::DEFAULT, 5);
|
gauge_style.set_radius(State::DEFAULT, 5);
|
||||||
gauge_style.set_bg_opa(State::DEFAULT, Opacity::OPA_COVER);
|
gauge_style.set_bg_opa(State::DEFAULT, Opacity::OPA_COVER);
|
||||||
gauge_style.set_bg_color(State::DEFAULT, Color::from_rgb((192, 192, 192)));
|
gauge_style.set_bg_color(State::DEFAULT, Color::from_rgb((192, 192, 192)));
|
||||||
// Set some paddings
|
// Set some padding's
|
||||||
gauge_style.set_pad_inner(State::DEFAULT, 20);
|
gauge_style.set_pad_inner(State::DEFAULT, 20);
|
||||||
gauge_style.set_pad_top(State::DEFAULT, 20);
|
gauge_style.set_pad_top(State::DEFAULT, 20);
|
||||||
gauge_style.set_pad_left(State::DEFAULT, 5);
|
gauge_style.set_pad_left(State::DEFAULT, 5);
|
||||||
|
|
52
examples/simple.rs
Normal file
52
examples/simple.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
use embedded_graphics_simulator::{
|
||||||
|
OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
|
||||||
|
};
|
||||||
|
use lvgl::widgets::Keyboard;
|
||||||
|
use lvgl::LvError;
|
||||||
|
use lvgl::UI;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
fn main() -> Result<(), LvError> {
|
||||||
|
let display: SimulatorDisplay<Rgb565> =
|
||||||
|
SimulatorDisplay::new(Size::new(lvgl::HOR_RES_MAX, lvgl::VER_RES_MAX));
|
||||||
|
|
||||||
|
let output_settings = OutputSettingsBuilder::new().scale(2).build();
|
||||||
|
let mut window = Window::new("Simple Example", &output_settings);
|
||||||
|
|
||||||
|
// Initialize LVGL
|
||||||
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
|
// Register your display
|
||||||
|
ui.disp_drv_register(display)?;
|
||||||
|
|
||||||
|
// Get the active screen
|
||||||
|
let mut screen = ui.scr_act()?;
|
||||||
|
|
||||||
|
// Create a Keyboard widget on the screen
|
||||||
|
let _ = Keyboard::new(&mut screen)?;
|
||||||
|
|
||||||
|
let mut loop_started = Instant::now();
|
||||||
|
'running: loop {
|
||||||
|
// Tell LVGL to process UI related tasks
|
||||||
|
ui.task_handler();
|
||||||
|
|
||||||
|
// Update your window with the latest display image
|
||||||
|
window.update(ui.get_display_ref().unwrap());
|
||||||
|
|
||||||
|
for event in window.events() {
|
||||||
|
match event {
|
||||||
|
SimulatorEvent::Quit => break 'running,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell LVGL how much time has past since last loop
|
||||||
|
ui.tick_inc(loop_started.elapsed());
|
||||||
|
|
||||||
|
loop_started = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lvgl-codegen"
|
name = "lvgl-codegen"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
description = "Code generation based on LVGL source code"
|
description = "Code generation based on LVGL source code"
|
||||||
authors = ["Rafael Caricio <crates.lvgl@caric.io>"]
|
authors = ["Rafael Caricio <crates.lvgl@caric.io>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -15,4 +15,3 @@ lazy_static = "1.4.0"
|
||||||
proc-macro2 = "1.0.18"
|
proc-macro2 = "1.0.18"
|
||||||
Inflector = "0.11.4"
|
Inflector = "0.11.4"
|
||||||
syn = { version = "1.0.31", features = ["full"]}
|
syn = { version = "1.0.31", features = ["full"]}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use inflector::cases::pascalcase::to_pascal_case;
|
use inflector::cases::pascalcase::to_pascal_case;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::format_ident;
|
use quote::{format_ident, ToTokens};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use syn::export::ToTokens;
|
|
||||||
use syn::{FnArg, ForeignItem, ForeignItemFn, Item, ReturnType};
|
use syn::{FnArg, ForeignItem, ForeignItemFn, Item, ReturnType};
|
||||||
|
|
||||||
type CGResult<T> = Result<T, Box<dyn Error>>;
|
type CGResult<T> = Result<T, Box<dyn Error>>;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lvgl-sys"
|
name = "lvgl-sys"
|
||||||
description = "Raw bindings to the LittlevGL C library."
|
description = "Raw bindings to the LittlevGL C library."
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
authors = ["Rafael Caricio <crates.lvgl-sys@caric.io>"]
|
authors = ["Rafael Caricio <crates.lvgl-sys@caric.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -62,25 +62,48 @@ fn main() {
|
||||||
.include(&lv_config_dir)
|
.include(&lv_config_dir)
|
||||||
.compile("lvgl");
|
.compile("lvgl");
|
||||||
|
|
||||||
let cc_args = [
|
let mut cc_args = vec![
|
||||||
"-DLV_CONF_INCLUDE_SIMPLE=1",
|
"-DLV_CONF_INCLUDE_SIMPLE=1",
|
||||||
"-I",
|
"-I",
|
||||||
lv_config_dir.to_str().unwrap(),
|
lv_config_dir.to_str().unwrap(),
|
||||||
"-I",
|
"-I",
|
||||||
vendor.to_str().unwrap(),
|
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());
|
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())
|
.header(shims_dir.join("lvgl_sys.h").to_str().unwrap())
|
||||||
.layout_tests(false)
|
.layout_tests(false)
|
||||||
.use_core()
|
.use_core()
|
||||||
.rustfmt_bindings(true)
|
.rustfmt_bindings(true)
|
||||||
.ctypes_prefix("cty")
|
.ctypes_prefix("cty")
|
||||||
.clang_args(&cc_args)
|
.clang_args(&cc_args)
|
||||||
|
.clang_args(&additional_args)
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings");
|
||||||
.write_to_file(out_path.join("bindings.rs"))
|
|
||||||
|
bindings.write_to_file(out_path.join("bindings.rs"))
|
||||||
.expect("Can't write bindings!");
|
.expect("Can't write bindings!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ keywords = ["littlevgl", "lvgl", "graphical_interfaces"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lvgl-sys = { version = "0.4.0", path = "../lvgl-sys" }
|
lvgl-sys = { version = "0.5.0", path = "../lvgl-sys" }
|
||||||
cty = "0.2.1"
|
cty = "0.2.1"
|
||||||
embedded-graphics = "0.6.2"
|
embedded-graphics = "0.6.2"
|
||||||
cstr_core = { version = "0.2.0" }
|
cstr_core = { version = "0.2.0" }
|
||||||
|
@ -21,6 +21,5 @@ bitflags = "1.2.1"
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
quote = "1.0.7"
|
quote = "1.0.7"
|
||||||
proc-macro2 = "1.0.18"
|
proc-macro2 = "1.0.18"
|
||||||
lvgl-codegen = { version = "0.4.0", path = "../lvgl-codegen" }
|
lvgl-codegen = { version = "0.5.0", path = "../lvgl-codegen" }
|
||||||
lvgl-sys = { version = "0.4.0", path = "../lvgl-sys" }
|
lvgl-sys = { version = "0.5.0", path = "../lvgl-sys" }
|
||||||
|
|
||||||
|
|
176
lvgl/src/input_device.rs
Normal file
176
lvgl/src/input_device.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
use crate::mem::Box;
|
||||||
|
use crate::LvResult;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
use embedded_graphics::geometry::Point;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub enum InputData {
|
||||||
|
Touch(Point),
|
||||||
|
Key(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputData {
|
||||||
|
pub fn released(self) -> InputState {
|
||||||
|
InputState::Released(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pressed(self) -> InputState {
|
||||||
|
InputState::Pressed(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub enum InputState {
|
||||||
|
Released(InputData),
|
||||||
|
Pressed(InputData),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputState {
|
||||||
|
pub fn once(self) -> BufferStatus {
|
||||||
|
BufferStatus::Once(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and_continued(self) -> BufferStatus {
|
||||||
|
BufferStatus::Buffered(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub enum BufferStatus {
|
||||||
|
Once(InputState),
|
||||||
|
Buffered(InputState),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pointer {
|
||||||
|
pub(crate) driver: lvgl_sys::lv_indev_drv_t,
|
||||||
|
pub(crate) descriptor: Option<lvgl_sys::lv_indev_t>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pointer {
|
||||||
|
pub fn new<F>(handler: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn() -> BufferStatus,
|
||||||
|
{
|
||||||
|
let driver = unsafe {
|
||||||
|
let mut indev_drv = MaybeUninit::uninit();
|
||||||
|
lvgl_sys::lv_indev_drv_init(indev_drv.as_mut_ptr());
|
||||||
|
let mut indev_drv = indev_drv.assume_init();
|
||||||
|
indev_drv.type_ = lvgl_sys::LV_INDEV_TYPE_POINTER as lvgl_sys::lv_indev_type_t;
|
||||||
|
indev_drv.read_cb = Some(read_input::<F>);
|
||||||
|
indev_drv.user_data = Box::into_raw(Box::new(handler).unwrap()) as *mut _
|
||||||
|
as lvgl_sys::lv_indev_drv_user_data_t;
|
||||||
|
indev_drv
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
driver,
|
||||||
|
descriptor: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn set_descriptor(
|
||||||
|
&mut self,
|
||||||
|
descriptor: *mut lvgl_sys::lv_indev_t,
|
||||||
|
) -> LvResult<()> {
|
||||||
|
// TODO: check if not null && check if `self.descriptor` is not already set!
|
||||||
|
self.descriptor = Some(*descriptor);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn read_input<F>(
|
||||||
|
indev_drv: *mut lvgl_sys::lv_indev_drv_t,
|
||||||
|
data: *mut lvgl_sys::lv_indev_data_t,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
F: Fn() -> BufferStatus,
|
||||||
|
{
|
||||||
|
// convert user data to function
|
||||||
|
let user_closure = &mut *((*indev_drv).user_data as *mut F);
|
||||||
|
// call user data
|
||||||
|
let info: BufferStatus = user_closure();
|
||||||
|
match info {
|
||||||
|
BufferStatus::Once(InputState::Pressed(InputData::Touch(point))) => {
|
||||||
|
(*data).point.x = point.x as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).point.y = point.y as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).state = lvgl_sys::LV_INDEV_STATE_PR as lvgl_sys::lv_indev_state_t;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
BufferStatus::Once(InputState::Released(InputData::Touch(point))) => {
|
||||||
|
(*data).point.x = point.x as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).point.y = point.y as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).state = lvgl_sys::LV_INDEV_STATE_REL as lvgl_sys::lv_indev_state_t;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
BufferStatus::Buffered(InputState::Pressed(InputData::Touch(point))) => {
|
||||||
|
(*data).point.x = point.x as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).point.y = point.y as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).state = lvgl_sys::LV_INDEV_STATE_PR as lvgl_sys::lv_indev_state_t;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
BufferStatus::Buffered(InputState::Released(InputData::Touch(point))) => {
|
||||||
|
(*data).point.x = point.x as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).point.y = point.y as lvgl_sys::lv_coord_t;
|
||||||
|
(*data).state = lvgl_sys::LV_INDEV_STATE_REL as lvgl_sys::lv_indev_state_t;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
BufferStatus::Once(InputState::Released(InputData::Key(_))) => false,
|
||||||
|
BufferStatus::Once(InputState::Pressed(InputData::Key(_))) => false,
|
||||||
|
BufferStatus::Buffered(InputState::Released(InputData::Key(_))) => true,
|
||||||
|
BufferStatus::Buffered(InputState::Pressed(InputData::Key(_))) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::UI;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embedded_graphics::drawable::Pixel;
|
||||||
|
use embedded_graphics::geometry::Size;
|
||||||
|
use embedded_graphics::pixelcolor::PixelColor;
|
||||||
|
use embedded_graphics::pixelcolor::Rgb565;
|
||||||
|
use embedded_graphics::DrawTarget;
|
||||||
|
|
||||||
|
struct FakeDisplay<C>
|
||||||
|
where
|
||||||
|
C: PixelColor,
|
||||||
|
{
|
||||||
|
p: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> DrawTarget<C> for FakeDisplay<C>
|
||||||
|
where
|
||||||
|
C: PixelColor,
|
||||||
|
{
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn draw_pixel(&mut self, item: Pixel<C>) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Size {
|
||||||
|
Size::new(crate::VER_RES_MAX, crate::HOR_RES_MAX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[test]
|
||||||
|
// We cannot test right now by having instances of UI global state... :(
|
||||||
|
// I need to find a way to test while having global state...
|
||||||
|
fn pointer_input_device() -> LvResult<()> {
|
||||||
|
let mut ui = UI::init()?;
|
||||||
|
|
||||||
|
let disp: FakeDisplay<Rgb565> = FakeDisplay { p: PhantomData };
|
||||||
|
|
||||||
|
ui.disp_drv_register(disp)?;
|
||||||
|
|
||||||
|
fn read_touchpad_device() -> BufferStatus {
|
||||||
|
InputData::Touch(Point::new(120, 23)).pressed().once()
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut touch_screen = Pointer::new(|| read_touchpad_device());
|
||||||
|
|
||||||
|
ui.indev_drv_register(&mut touch_screen)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
|
pub mod input_device;
|
||||||
pub(crate) mod mem;
|
pub(crate) mod mem;
|
||||||
mod support;
|
mod support;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
@ -13,3 +14,6 @@ pub mod widgets;
|
||||||
pub use lv_core::*;
|
pub use lv_core::*;
|
||||||
pub use support::*;
|
pub use support::*;
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
|
|
||||||
|
pub const HOR_RES_MAX: u32 = lvgl_sys::LV_HOR_RES_MAX;
|
||||||
|
pub const VER_RES_MAX: u32 = lvgl_sys::LV_VER_RES_MAX;
|
||||||
|
|
|
@ -70,16 +70,17 @@ impl<T> AsMut<T> for Box<T> {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
static INIT_LVGL: Once = Once::new();
|
|
||||||
|
|
||||||
fn init() {
|
fn init() {
|
||||||
INIT_LVGL.call_once(|| {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
lvgl_sys::lv_init();
|
lvgl_sys::lv_init();
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
fn teardown() {
|
||||||
|
unsafe {
|
||||||
|
lvgl_sys::lv_deinit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -90,6 +91,8 @@ mod test {
|
||||||
drop(v);
|
drop(v);
|
||||||
let v = Box::new(10).unwrap();
|
let v = Box::new(10).unwrap();
|
||||||
drop(v);
|
drop(v);
|
||||||
|
|
||||||
|
teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -129,6 +132,8 @@ mod test {
|
||||||
}
|
}
|
||||||
assert_eq!(point.x, i);
|
assert_eq!(point.x, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_mem_info() {
|
fn print_mem_info() {
|
||||||
|
|
|
@ -27,6 +27,24 @@ impl Color {
|
||||||
pub fn from_raw(raw: lvgl_sys::lv_color_t) -> Self {
|
pub fn from_raw(raw: lvgl_sys::lv_color_t) -> Self {
|
||||||
Self { raw }
|
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 {
|
impl From<Color> for Rgb888 {
|
||||||
|
@ -61,6 +79,7 @@ impl From<Color> for Rgb565 {
|
||||||
///
|
///
|
||||||
/// All objects (such as Buttons/Labels/Sliders etc.) receive these generic events
|
/// All objects (such as Buttons/Labels/Sliders etc.) receive these generic events
|
||||||
/// regardless of their type.
|
/// regardless of their type.
|
||||||
|
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub enum Event<T> {
|
pub enum Event<T> {
|
||||||
/// The object has been pressed
|
/// The object has been pressed
|
||||||
Pressed,
|
Pressed,
|
||||||
|
@ -140,6 +159,7 @@ impl<S> From<Event<S>> for lvgl_sys::lv_event_t {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// These events are sent only by pointer-like input devices (E.g. mouse or touchpad)
|
/// These events are sent only by pointer-like input devices (E.g. mouse or touchpad)
|
||||||
|
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub enum PointerEvent {
|
pub enum PointerEvent {
|
||||||
DragBegin,
|
DragBegin,
|
||||||
DragEnd,
|
DragEnd,
|
||||||
|
@ -231,3 +251,25 @@ impl From<Animation> for lvgl_sys::lv_anim_enable_t {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use lvgl_sys;
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::input_device::Pointer;
|
||||||
use crate::mem::Box;
|
use crate::mem::Box;
|
||||||
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
use crate::{Color, Event, LvError, LvResult, Obj, Widget};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
@ -56,6 +57,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn indev_drv_register(&mut self, input_device: &mut Pointer) -> LvResult<()> {
|
||||||
|
if self.display_data.is_none() {
|
||||||
|
// TODO: Better yet would be to create a display struct that one register the
|
||||||
|
// input device in that instance. Represents better the LVGL correct usage. Also it's
|
||||||
|
// inline with unrepresentable invalid states using Rust type system.
|
||||||
|
// ```rust
|
||||||
|
// let disp = ui.disp_drv_register(embed_graph_disp)?;
|
||||||
|
// disp.indev_drv_register(disp);
|
||||||
|
// ...
|
||||||
|
// window.update(&disp)
|
||||||
|
// ```
|
||||||
|
return Err(LvError::Uninitialized);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let descr = lvgl_sys::lv_indev_drv_register(&mut input_device.driver as *mut _);
|
||||||
|
input_device.set_descriptor(descr)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disp_drv_register(&mut self, display: T) -> LvResult<()> {
|
pub fn disp_drv_register(&mut self, display: T) -> LvResult<()> {
|
||||||
self.display_data = Some(DisplayUserData {
|
self.display_data = Some(DisplayUserData {
|
||||||
display,
|
display,
|
||||||
|
|
Loading…
Reference in a new issue