diff --git a/README.md b/README.md index c6d7f07..c2394ed 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,12 @@ $ DEP_LV_CONFIG_PATH=`pwd` cargo build -Zfeatures=build_dep ## 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) First, make sure to pull `lvgl-rs` submodules: diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 5e116c8..52dd934 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -13,6 +13,10 @@ embedded-graphics-simulator = "0.2.0" heapless = "0.5.5" cstr_core = { version = "0.2.0", features = ["alloc"] } +[[example]] +name = "simple" +path = "simple.rs" + [[example]] name = "demo" path = "demo.rs" diff --git a/examples/arc.rs b/examples/arc.rs index 05523a4..52183ef 100644 --- a/examples/arc.rs +++ b/examples/arc.rs @@ -8,14 +8,11 @@ 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 main() -> Result<(), LvError> { - let display: SimulatorDisplay = SimulatorDisplay::new(Size::new( - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - )); + let display: SimulatorDisplay = + 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("Arc Example", &output_settings); diff --git a/examples/bar.rs b/examples/bar.rs index 6472b74..ea428d5 100644 --- a/examples/bar.rs +++ b/examples/bar.rs @@ -7,14 +7,11 @@ use embedded_graphics_simulator::{ 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 = SimulatorDisplay::new(Size::new( - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - )); + let display: SimulatorDisplay = + 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("Bar Example", &output_settings); diff --git a/examples/button_click.rs b/examples/button_click.rs index 58d6980..f16e567 100644 --- a/examples/button_click.rs +++ b/examples/button_click.rs @@ -4,26 +4,34 @@ use embedded_graphics::prelude::*; use embedded_graphics_simulator::{ OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window, }; + +use lvgl::input_device::{BufferStatus, InputData, Pointer}; 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; +use lvgl::{self, Align, Color, LvError, Part, State, Widget, UI}; + +use std::thread::sleep; +use std::time::{Duration, Instant}; fn main() -> Result<(), LvError> { - let display: SimulatorDisplay = SimulatorDisplay::new(Size::new( - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - )); + let display: SimulatorDisplay = + 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("Bar Example", &output_settings); let mut ui = UI::init()?; - // Implement and register your display: + // Register your 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 let mut screen = ui.scr_act()?; @@ -40,6 +48,7 @@ fn main() -> Result<(), LvError> { let mut btn_state = false; button.on_event(|mut btn, event| { + println!("Button received event: {:?}", event); if let lvgl::Event::Clicked = event { if btn_state { 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_state = !btn_state; - println!("Clicked! Inner.."); btn.toggle().unwrap(); } })?; let mut loop_started = Instant::now(); + let mut latest_touch_point = Point::new(0, 0); 'running: loop { ui.task_handler(); 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 { SimulatorEvent::MouseButtonUp { mouse_btn: _, @@ -67,13 +84,16 @@ fn main() -> Result<(), LvError> { } => { println!("Clicked on: {:?}", point); // 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, _ => {} } } + sleep(Duration::from_millis(15)); + ui.tick_inc(loop_started.elapsed()); loop_started = Instant::now(); } diff --git a/examples/demo.rs b/examples/demo.rs index 60a6be5..b236c2b 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -13,10 +13,8 @@ use std::thread::sleep; use std::time::{Duration, Instant}; fn main() -> Result<(), LvError> { - let display: SimulatorDisplay = SimulatorDisplay::new(Size::new( - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - )); + let display: SimulatorDisplay = + 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("PineTime", &output_settings); diff --git a/examples/gauge.rs b/examples/gauge.rs index 508e58b..ca236d4 100644 --- a/examples/gauge.rs +++ b/examples/gauge.rs @@ -6,14 +6,11 @@ use embedded_graphics_simulator::{ 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 = SimulatorDisplay::new(Size::new( - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - )); + let display: SimulatorDisplay = + 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("Gauge Example", &output_settings); @@ -36,7 +33,7 @@ fn main() -> Result<(), LvError> { 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 + // Set some padding's gauge_style.set_pad_inner(State::DEFAULT, 20); gauge_style.set_pad_top(State::DEFAULT, 20); gauge_style.set_pad_left(State::DEFAULT, 5); diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..1a05bd0 --- /dev/null +++ b/examples/simple.rs @@ -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 = + 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(()) +} diff --git a/lvgl-codegen/Cargo.toml b/lvgl-codegen/Cargo.toml index c26278b..81d5ea1 100644 --- a/lvgl-codegen/Cargo.toml +++ b/lvgl-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lvgl-codegen" -version = "0.4.0" +version = "0.5.0" description = "Code generation based on LVGL source code" authors = ["Rafael Caricio "] readme = "README.md" @@ -15,4 +15,3 @@ lazy_static = "1.4.0" proc-macro2 = "1.0.18" Inflector = "0.11.4" syn = { version = "1.0.31", features = ["full"]} - diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index b6b8132..eaf83bd 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -1,12 +1,11 @@ use inflector::cases::pascalcase::to_pascal_case; use lazy_static::lazy_static; use proc_macro2::{Ident, TokenStream}; -use quote::format_ident; +use quote::{format_ident, ToTokens}; use quote::quote; use regex::Regex; use std::collections::HashMap; use std::error::Error; -use syn::export::ToTokens; use syn::{FnArg, ForeignItem, ForeignItemFn, Item, ReturnType}; type CGResult = Result>; diff --git a/lvgl-sys/Cargo.toml b/lvgl-sys/Cargo.toml index a2d9690..37f6af0 100644 --- a/lvgl-sys/Cargo.toml +++ b/lvgl-sys/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lvgl-sys" description = "Raw bindings to the LittlevGL C library." -version = "0.4.0" +version = "0.5.0" authors = ["Rafael Caricio "] edition = "2018" license = "MIT" diff --git a/lvgl-sys/build.rs b/lvgl-sys/build.rs index 19c14ff..9bc0450 100644 --- a/lvgl-sys/build.rs +++ b/lvgl-sys/build.rs @@ -62,25 +62,48 @@ fn main() { .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()) .layout_tests(false) .use_core() .rustfmt_bindings(true) .ctypes_prefix("cty") .clang_args(&cc_args) + .clang_args(&additional_args) .generate() - .expect("Unable to generate bindings") - .write_to_file(out_path.join("bindings.rs")) + .expect("Unable to generate bindings"); + + bindings.write_to_file(out_path.join("bindings.rs")) .expect("Can't write bindings!"); } diff --git a/lvgl/Cargo.toml b/lvgl/Cargo.toml index 2af077e..ffdcc90 100644 --- a/lvgl/Cargo.toml +++ b/lvgl/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["littlevgl", "lvgl", "graphical_interfaces"] build = "build.rs" [dependencies] -lvgl-sys = { version = "0.4.0", path = "../lvgl-sys" } +lvgl-sys = { version = "0.5.0", path = "../lvgl-sys" } cty = "0.2.1" embedded-graphics = "0.6.2" cstr_core = { version = "0.2.0" } @@ -21,6 +21,5 @@ bitflags = "1.2.1" [build-dependencies] quote = "1.0.7" proc-macro2 = "1.0.18" -lvgl-codegen = { version = "0.4.0", path = "../lvgl-codegen" } -lvgl-sys = { version = "0.4.0", path = "../lvgl-sys" } - +lvgl-codegen = { version = "0.5.0", path = "../lvgl-codegen" } +lvgl-sys = { version = "0.5.0", path = "../lvgl-sys" } diff --git a/lvgl/src/input_device.rs b/lvgl/src/input_device.rs new file mode 100644 index 0000000..5cec36c --- /dev/null +++ b/lvgl/src/input_device.rs @@ -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, +} + +impl Pointer { + pub fn new(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::); + 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( + 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 + where + C: PixelColor, + { + p: PhantomData, + } + + impl DrawTarget for FakeDisplay + where + C: PixelColor, + { + type Error = (); + + fn draw_pixel(&mut self, item: Pixel) -> 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 = 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(()) + } +} diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index 8042594..6ae501f 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -3,6 +3,7 @@ #[macro_use] extern crate bitflags; +pub mod input_device; pub(crate) mod mem; mod support; mod ui; @@ -13,3 +14,6 @@ pub mod widgets; pub use lv_core::*; pub use support::*; 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; diff --git a/lvgl/src/mem.rs b/lvgl/src/mem.rs index 9167858..e041cd4 100644 --- a/lvgl/src/mem.rs +++ b/lvgl/src/mem.rs @@ -70,16 +70,17 @@ impl AsMut for Box { mod test { use super::*; use core::mem::MaybeUninit; - use std::sync::Once; - - static INIT_LVGL: Once = Once::new(); fn init() { - INIT_LVGL.call_once(|| { - unsafe { - lvgl_sys::lv_init(); - }; - }); + unsafe { + lvgl_sys::lv_init(); + }; + } + + fn teardown() { + unsafe { + lvgl_sys::lv_deinit(); + } } #[test] @@ -90,6 +91,8 @@ mod test { drop(v); let v = Box::new(10).unwrap(); drop(v); + + teardown(); } #[test] @@ -129,6 +132,8 @@ mod test { } assert_eq!(point.x, i); } + + teardown(); } fn print_mem_info() { diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index e614de0..bc9b0fb 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -27,6 +27,24 @@ 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 for Rgb888 { @@ -61,6 +79,7 @@ impl From for Rgb565 { /// /// All objects (such as Buttons/Labels/Sliders etc.) receive these generic events /// regardless of their type. +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum Event { /// The object has been pressed Pressed, @@ -140,6 +159,7 @@ impl From> for lvgl_sys::lv_event_t { } /// 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 { DragBegin, DragEnd, @@ -231,3 +251,25 @@ impl From 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); + } + } +} diff --git a/lvgl/src/ui.rs b/lvgl/src/ui.rs index 699741c..aa4ab3d 100644 --- a/lvgl/src/ui.rs +++ b/lvgl/src/ui.rs @@ -1,3 +1,4 @@ +use crate::input_device::Pointer; use crate::mem::Box; use crate::{Color, Event, LvError, LvResult, Obj, Widget}; 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<()> { self.display_data = Some(DisplayUserData { display,