diff --git a/examples/demo/Cargo.toml b/examples/demo/Cargo.toml index 3493850..0d08288 100644 --- a/examples/demo/Cargo.toml +++ b/examples/demo/Cargo.toml @@ -10,4 +10,5 @@ publish = false [dependencies] lvgl = { path = "../../lvgl" } lvgl-sys = { path = "../../lvgl-sys" } -sdl2 = "0.33.0" +embedded-graphics = "0.6" +embedded-graphics-simulator = "0.2.0" diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index f06bf3a..f5b109b 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -1,68 +1,30 @@ use lvgl; use lvgl::Object; use lvgl_sys; -use sdl2::event::Event; -use sdl2::keyboard::Keycode; -use sdl2::pixels::Color; -use sdl2::rect::Point; -use std::mem::MaybeUninit; -use std::os::raw::c_void; -use std::panic; use std::time::Duration; +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::mock_display::MockDisplay; +use embedded_graphics::prelude::*; +use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window, BinaryColorTheme, SimulatorEvent}; fn main() -> Result<(), String> { - let sdl_context = sdl2::init()?; - let video_subsystem = sdl_context.video()?; - let mut framebuffer = [[Color::from((0, 0, 0)); lvgl_sys::LV_VER_RES_MAX as usize]; - lvgl_sys::LV_HOR_RES_MAX as usize]; + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(lvgl_sys::LV_HOR_RES_MAX,lvgl_sys::LV_VER_RES_MAX)); - let window = video_subsystem - .window( - "TFT Display: Demo", - lvgl_sys::LV_HOR_RES_MAX, - lvgl_sys::LV_VER_RES_MAX, - ) - .position_centered() - .opengl() - .build() - .map_err(|e| e.to_string())?; - - let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?; - - canvas.set_draw_color(Color::RGB(0, 0, 0)); - canvas.clear(); - canvas.present(); + let output_settings = OutputSettingsBuilder::new() + .theme(BinaryColorTheme::OledBlue) + .build(); + let mut window = Window::new("Hello World", &output_settings); unsafe { lvgl_sys::lv_init(); } - // Implement and register a function which can copy a pixel array to an area of your display: - let mut display_driver = DisplayDriver::new(move |points, colors| { - for (i, point) in points.into_iter().enumerate() { - let color = &mut framebuffer[point.x() as usize][point.y() as usize]; - *color = colors[i].clone(); - } - canvas.clear(); - for (x, line) in framebuffer.iter().enumerate() { - for (y, color) in line.iter().enumerate() { - canvas.set_draw_color(color.clone()); - canvas.draw_point(Point::new(x as i32, y as i32)).unwrap(); - } - } - canvas.present(); - }); + // Implement and register your display: + let mut display_driver = lvgl::DisplayDriver::new(&mut display); // Create screen and widgets let mut screen = display_driver.get_active_screen(); - // let mut button = lvgl::Button::new(&mut screen); - // button.set_pos(50, 50); - // button.set_size(100, 50); - // - // let mut label = lvgl::Label::new(&mut button); - // label.set_text("Hello Mundo!\0"); - let font_roboto_28 = unsafe { &lvgl_sys::lv_font_roboto_28 }; let font_noto_sans_numeric_28 = unsafe { ¬o_sans_numeric_80 }; @@ -102,22 +64,9 @@ fn main() -> Result<(), String> { power.set_label_align(lvgl::LabelAlign::Right); power.set_align(&mut screen, lvgl::Align::InTopRight, 0, 0); - let mut event_pump = sdl_context.event_pump()?; + let mut i = 0; 'running: loop { - if let Some(event) = event_pump.poll_event() { - match event { - Event::Quit { .. } - | Event::KeyDown { - keycode: Some(Keycode::Escape), - .. - } => { - break 'running; - } - _ => {} - } - } - if i > 59 { i = 0; } @@ -130,6 +79,14 @@ fn main() -> Result<(), String> { lvgl_sys::lv_task_handler(); lvgl_sys::lv_tick_inc(10); } + window.update(&display); + + for event in window.events() { + match event { + SimulatorEvent::Quit => break 'running, + _ => {} + } + } } Ok(()) @@ -139,102 +96,3 @@ fn main() -> Result<(), String> { extern "C" { pub static mut noto_sans_numeric_80: lvgl_sys::lv_font_t; } - -#[allow(dead_code)] -struct DisplayDriver -where - F: FnMut(Vec, Vec), -{ - pub raw: lvgl_sys::lv_disp_drv_t, - callback: F, - display_buffer: MaybeUninit, - refresh_buffer: [MaybeUninit; lvgl_sys::LV_HOR_RES_MAX as usize * 10], -} - -impl DisplayDriver -where - F: FnMut(Vec, Vec), -{ - fn new(mut callback: F) -> Self { - // Create a display buffer for LittlevGL - let mut display_buffer = MaybeUninit::::uninit(); - let mut refresh_buffer: [MaybeUninit; - lvgl_sys::LV_HOR_RES_MAX as usize * 10] = - unsafe { MaybeUninit::uninit().assume_init() }; /*Declare a buffer for 10 lines*/ - unsafe { - // Initialize the display buffer - lvgl_sys::lv_disp_buf_init( - display_buffer.as_mut_ptr(), - refresh_buffer.as_mut_ptr() as *mut c_void, - std::ptr::null_mut(), - (lvgl_sys::LV_HOR_RES_MAX * 10) as u32, - ); - } - let mut disp_drv = unsafe { - let mut disp_drv = MaybeUninit::::uninit().assume_init(); /*Descriptor of a display driver*/ - lvgl_sys::lv_disp_drv_init(&mut disp_drv); // Basic initialization - disp_drv.flush_cb = Some(display_callback_wrapper::); // Set your driver function - disp_drv.user_data = &mut callback as *mut _ as *mut c_void; - disp_drv - }; - disp_drv.buffer = display_buffer.as_mut_ptr(); // Assign the buffer to the display - unsafe { - lvgl_sys::lv_disp_drv_register(&mut disp_drv); // Finally register the driver - } - Self { - raw: disp_drv, - callback, - display_buffer, - refresh_buffer, - } - } - - fn get_active_screen(&mut self) -> lvgl::ObjectX<'static> { - lvgl::display::get_active_screen() - } -} - -unsafe extern "C" fn display_callback_wrapper( - 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 - F: FnMut(Vec, Vec), -{ - // We need to make sure panics can't escape across the FFI boundary. - let _ = panic::catch_unwind(|| { - let mut i = 0; - let disp = *disp_drv; - - // Rust code closure reference - let closure = &mut *(disp.user_data as *mut F); - - let mut points = vec![]; - let mut colors = vec![]; - - for y in (*area).y1..=(*area).y2 { - for x in (*area).x1..=(*area).x2 { - // Convert point to paint to a high-level Rust repr - points.push(Point::new(x as i32, y as i32)); - - // Convert C color representation to high-level Rust - let raw_color = *color_p.add(i); - let color = Color::from(( - raw_color.ch.red, - raw_color.ch.green, - raw_color.ch.blue, - raw_color.ch.alpha, - )); - colors.push(color); - - i = i + 1; - } - } - - // Callback the Rust closure to flush the new points to the screen - closure(points, colors); - - // Indicate to LittlevGL that you are ready with the flushing - lvgl_sys::lv_disp_flush_ready(disp_drv); - }); -} diff --git a/lvgl/Cargo.toml b/lvgl/Cargo.toml index f1e3bf2..db865c1 100644 --- a/lvgl/Cargo.toml +++ b/lvgl/Cargo.toml @@ -13,5 +13,6 @@ keywords = ["littlevgl", "lvgl", "graphical_interfaces"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lvgl-sys = {path="../lvgl-sys", version="0.1.1"} -cty = "0.2.1" +lvgl-sys = {path="../lvgl-sys", version="0.1"} +cty = "0.2" +embedded-graphics = "0.6" diff --git a/lvgl/src/display.rs b/lvgl/src/display.rs index 43dea41..199be9b 100644 --- a/lvgl/src/display.rs +++ b/lvgl/src/display.rs @@ -1,5 +1,100 @@ use crate::objx::ObjectX; use core::ptr; +use embedded_graphics; +use embedded_graphics::prelude::*; +use embedded_graphics::{drawable, DrawTarget}; +use core::mem::MaybeUninit; +use embedded_graphics::pixelcolor::Rgb888; +use core::marker::PhantomData; + + +pub struct DisplayDriver<'a, T> + where + T: DrawTarget, +{ + raw: lvgl_sys::lv_disp_drv_t, + display_buffer: MaybeUninit, + refresh_buffer: [MaybeUninit; lvgl_sys::LV_HOR_RES_MAX as usize * 10], + phantom: &'a PhantomData, +} + +impl<'a, T> DisplayDriver<'a, T> where + T: DrawTarget +{ + pub fn new(device: &'a mut T) -> Self { + // Create a display buffer for LittlevGL + let mut display_buffer = MaybeUninit::::uninit(); + // Declare a buffer for 10 lines + let mut refresh_buffer: [MaybeUninit; + lvgl_sys::LV_HOR_RES_MAX as usize * 10] = + unsafe { MaybeUninit::uninit().assume_init() }; + // Initialize the display buffer + unsafe { + lvgl_sys::lv_disp_buf_init( + display_buffer.as_mut_ptr(), + refresh_buffer.as_mut_ptr() as *mut cty::c_void, + core::ptr::null_mut(), + (lvgl_sys::LV_HOR_RES_MAX * 10) as u32, + ); + } + let mut disp_drv = unsafe { + // Descriptor of a display driver + let mut disp_drv = MaybeUninit::::uninit().assume_init(); + // Basic initialization + lvgl_sys::lv_disp_drv_init(&mut disp_drv); + // Set your driver function + disp_drv.flush_cb = Some(display_callback_wrapper::); + disp_drv.user_data = device as *mut _ as *mut cty::c_void; + disp_drv + }; + // Assign the buffer to the display + disp_drv.buffer = display_buffer.as_mut_ptr(); + // Finally register the driver + unsafe { + lvgl_sys::lv_disp_drv_register(&mut disp_drv); + } + Self { + raw: disp_drv, + display_buffer, + refresh_buffer, + phantom: &PhantomData, + } + } + + pub fn get_active_screen(&mut self) -> ObjectX<'static> { + get_active_screen() + } +} + +unsafe extern "C" fn display_callback_wrapper( + 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, +{ + // We need to make sure panics can't escape across the FFI boundary. + //let _ = panic::catch_unwind(|| { + let mut i = 0; + let disp = *disp_drv; + + // Rust code closure reference + let device = &mut *(disp.user_data as *mut T); + + for y in (*area).y1..=(*area).y2 { + for x in (*area).x1..=(*area).x2 { + // Convert C color representation to high-level Rust + let raw_color = *color_p.add(i); + i = i + 1; + let color = Rgb888::new(raw_color.ch.red, raw_color.ch.green, raw_color.ch.blue); + // Callback the Rust closure to flush the new points to the screen + let _ = device.draw_pixel(drawable::Pixel(Point::new(x as i32, y as i32), color)); + } + } + // Indicate to LittlevGL that you are ready with the flushing + lvgl_sys::lv_disp_flush_ready(disp_drv); + //}); // end of panic::catch_unwind +} pub fn get_active_screen() -> ObjectX<'static> { let raw = diff --git a/lvgl/src/lib.rs b/lvgl/src/lib.rs index f9efaa6..aa5e592 100644 --- a/lvgl/src/lib.rs +++ b/lvgl/src/lib.rs @@ -3,4 +3,5 @@ pub mod display; mod objx; +pub use display::DisplayDriver; pub use objx::*;