diff --git a/examples/animation.rs b/examples/animation.rs index 3d15f1f7..f6d2f74c 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -12,8 +12,8 @@ use lvgl::input_device::{ }; use lvgl::misc::anim::{AnimRepeatCount, Animation}; use lvgl::style::Style; -use lvgl::widgets::{Btn, Label}; -use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part, Widget}; +use lvgl::widgets::{Btn, Label, Widget}; +use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part}; use std::thread::sleep; use std::time::Duration; use std::time::Instant; @@ -46,18 +46,18 @@ fn main() -> Result<(), LvError> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); - screen.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the button let mut button = Btn::create(&mut screen)?; - button.set_align(Align::LeftMid, 30, 0); + button.align(Align::LeftMid.into(), 30, 0); button.set_size(180, 80); let mut btn_lbl = Label::create(&mut button)?; - btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str())?; + btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str()); let mut btn_state = false; let mut anim = Animation::new(&mut button, Duration::from_secs(1), 0, 60, |obj, val| { - obj.set_align(Align::LeftMid, val, 0) + obj.align(Align::LeftMid.into(), val as i16, 0) })?; anim.set_repeat_count(AnimRepeatCount::Infinite); anim.start(); @@ -66,10 +66,10 @@ fn main() -> Result<(), LvError> { 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(); + btn_lbl.set_text(nt.as_c_str()); } else { let nt = CString::new("Clicked!").unwrap(); - btn_lbl.set_text(nt.as_c_str()).unwrap(); + btn_lbl.set_text(nt.as_c_str()); } btn_state = !btn_state; } diff --git a/examples/arc.rs b/examples/arc.rs index 8e10b86a..cc89a607 100644 --- a/examples/arc.rs +++ b/examples/arc.rs @@ -6,8 +6,8 @@ use embedded_graphics_simulator::{ }; use lvgl; use lvgl::style::Style; -use lvgl::widgets::{Arc, Label}; -use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part, Widget}; +use lvgl::widgets::{Arc, Label, Widget}; +use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part}; use lvgl_sys; use std::thread::sleep; use std::time::Duration; @@ -52,23 +52,23 @@ fn main() -> Result<(), LvError> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((255, 255, 255))); screen_style.set_radius(0); - screen.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the arc object let mut arc = Arc::create(&mut screen)?; arc.set_size(150, 150); - arc.set_align(Align::Center, 0, 10); + arc.align(Align::Center.into(), 0, 10); arc.set_start_angle(135); arc.set_end_angle(135); let mut loading_lbl = Label::create(&mut screen)?; - loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?; - loading_lbl.set_align(Align::OutTopMid, 0, 0); + loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str()); + loading_lbl.align(Align::OutTopMid.into(), 0, 0); //loading_lbl.set_label_align(LabelAlign::Center)?; let mut loading_style = Style::default(); loading_style.set_text_color(Color::from_rgb((0, 0, 0))); - loading_lbl.add_style(Part::Main, &mut loading_style); + loading_lbl.add_style(loading_style.into_raw(), Part::Main.into()); let mut angle = 0; let mut forward = true; @@ -82,7 +82,7 @@ fn main() -> Result<(), LvError> { println!("mem info running: {:?}", mem_info()); } angle = if forward { angle + 1 } else { angle - 1 }; - arc.set_end_angle(angle + 135)?; + arc.set_end_angle(angle + 135); i += 1; lvgl::task_handler(); diff --git a/examples/bar.rs b/examples/bar.rs index 9012f599..fd20743b 100644 --- a/examples/bar.rs +++ b/examples/bar.rs @@ -6,8 +6,8 @@ use embedded_graphics_simulator::{ }; use lvgl; use lvgl::style::Style; -use lvgl::widgets::{Bar, Label}; -use lvgl::{Align, AnimationState, Color, Display, DrawBuffer, Event, LvError, Part, Widget}; +use lvgl::widgets::{Bar, Label, Widget}; +use lvgl::{Align, AnimationState, Color, Display, DrawBuffer, Event, LvError, Part}; use std::thread::sleep; use std::time::Duration; use std::time::Instant; @@ -33,13 +33,13 @@ fn main() -> Result<(), LvError> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((255, 255, 255))); screen_style.set_radius(0); - screen.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the bar object let mut bar = Bar::create(&mut screen)?; bar.set_size(175, 20); - bar.set_align(Align::Center, 0, 10); - bar.set_range(0, 100)?; + bar.align(Align::Center.into(), 0, 10); + bar.set_range(0, 100); bar.on_event(|_b, _e| { println!("Completed!"); })?; @@ -47,15 +47,15 @@ fn main() -> Result<(), LvError> { // Set the indicator style for the bar object let mut ind_style = Style::default(); ind_style.set_bg_color(Color::from_rgb((100, 245, 100))); - bar.add_style(Part::Any, &mut ind_style); + bar.add_style(ind_style.into_raw(), Part::Any.into()); let mut loading_lbl = Label::create(&mut screen)?; - loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?; - loading_lbl.set_align(Align::OutTopMid, 0, 0); + loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str()); + loading_lbl.align(Align::OutTopMid.into(), 0, 0); let mut loading_style = Style::default(); loading_style.set_text_color(Color::from_rgb((0, 0, 0))); - loading_lbl.add_style(Part::Main, &mut loading_style); + loading_lbl.add_style(loading_style.into_raw(), Part::Main.into()); let mut i = 0; 'running: loop { @@ -67,7 +67,7 @@ fn main() -> Result<(), LvError> { // - implementation of `Widget` is not general enough // lvgl::event_send(&mut bar, Event::Clicked); } - bar.set_value(i, AnimationState::ON); + bar.set_value(i, AnimationState::ON.into()); i += 1; lvgl::task_handler(); diff --git a/examples/button_click.rs b/examples/button_click.rs index 30c0a4c1..99b33fc1 100644 --- a/examples/button_click.rs +++ b/examples/button_click.rs @@ -11,8 +11,8 @@ use lvgl::input_device::{ InputDriver, }; use lvgl::style::Style; -use lvgl::widgets::{Btn, Label}; -use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part, Widget}; +use lvgl::widgets::{Btn, Label, Widget}; +use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part}; use std::thread::sleep; use std::time::Duration; use std::time::Instant; @@ -45,13 +45,13 @@ fn main() -> Result<(), LvError> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); - screen.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the button let mut button = Btn::create(&mut screen)?; - button.set_align(Align::LeftMid, 30, 0); + button.align(Align::LeftMid.into(), 30, 0); button.set_size(180, 80); let mut btn_lbl = Label::create(&mut button)?; - btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str())?; + btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str()); let mut btn_state = false; button.on_event(|_btn, event| { @@ -59,10 +59,10 @@ fn main() -> Result<(), LvError> { 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(); + btn_lbl.set_text(nt.as_c_str()); } else { let nt = CString::new("Clicked!").unwrap(); - btn_lbl.set_text(nt.as_c_str()).unwrap(); + btn_lbl.set_text(nt.as_c_str()); } btn_state = !btn_state; } diff --git a/examples/demo.rs b/examples/demo.rs index 21537c8e..d542153d 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -7,8 +7,8 @@ use embedded_graphics_simulator::{ use lvgl; use lvgl::font::Font; use lvgl::style::Style; -use lvgl::widgets::Label; -use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part, TextAlign, Widget}; +use lvgl::widgets::{Label, Widget}; +use lvgl::{Align, Color, Display, DrawBuffer, LvError, Part, TextAlign}; use lvgl_sys; use std::thread::sleep; use std::time::Duration; @@ -35,14 +35,14 @@ fn main() -> Result<(), LvError> { // Create screen and widgets let binding = display?; - let screen = binding.get_scr_act(); + let mut screen = binding.get_scr_act()?; println!("Before all widgets: {:?}", mem_info()); let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); screen_style.set_radius(0); - screen?.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); let mut time = Label::from("20:46"); let mut style_time = Style::default(); @@ -52,8 +52,8 @@ fn main() -> Result<(), LvError> { // See font module documentation for an explanation of the unsafe block style_time.set_text_font(unsafe { Font::new_raw(lvgl_sys::noto_sans_numeric_80) }); - time.add_style(Part::Main, &mut style_time); - time.set_align(Align::Center, 0, 90); + time.add_style(style_time.into_raw(), Part::Main.into()); + time.align(Align::Center.into(), 0, 90); time.set_width(240); time.set_height(240); @@ -61,13 +61,13 @@ fn main() -> Result<(), LvError> { bt.set_width(50); bt.set_height(80); let _ = bt.set_recolor(true); - bt.set_align(Align::TopLeft, 0, 0); + bt.align(Align::TopLeft.into(), 0, 0); let mut power: Label = "#fade2a 20%#".into(); let _ = power.set_recolor(true); power.set_width(80); power.set_height(20); - power.set_align(Align::TopRight, 40, 0); + power.align(Align::TopRight.into(), 40, 0); let mut i = 0; 'running: loop { diff --git a/examples/rust_timer.rs b/examples/rust_timer.rs index 6a26464b..5c647c83 100644 --- a/examples/rust_timer.rs +++ b/examples/rust_timer.rs @@ -6,8 +6,8 @@ use embedded_graphics_simulator::{ }; use lvgl; use lvgl::style::Style; -use lvgl::widgets::{Bar, Label}; -use lvgl::{Align, AnimationState, Color, Display, DrawBuffer, Event, LvError, Part, Widget}; +use lvgl::widgets::{Bar, Label, Widget}; +use lvgl::{Align, AnimationState, Color, Display, DrawBuffer, Event, LvError, Part}; use std::thread::sleep; use std::time::Duration; use std::time::Instant; @@ -53,13 +53,13 @@ fn main() -> Result<(), LvError> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((255, 255, 255))); screen_style.set_radius(0); - screen.add_style(Part::Main, &mut screen_style)?; + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the bar object let mut bar = Bar::create(&mut screen)?; - bar.set_size(175, 20)?; - bar.set_align(Align::Center, 0, 10)?; - bar.set_range(0, 100)?; + bar.set_size(175, 20); + bar.align(Align::Center.into(), 0, 10); + bar.set_range(0, 100); bar.on_event(|_b, _e| { println!("Completed!"); })?; @@ -67,24 +67,24 @@ fn main() -> Result<(), LvError> { // Set the indicator style for the bar object let mut ind_style = Style::default(); ind_style.set_bg_color(Color::from_rgb((100, 245, 100))); - bar.add_style(Part::Any, &mut ind_style)?; + bar.add_style(ind_style.into_raw(), Part::Main.into()); let mut loading_lbl = Label::create(&mut screen)?; - loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str())?; - loading_lbl.set_align(Align::OutTopMid, 0, 0)?; + loading_lbl.set_text(CString::new("Loading...").unwrap().as_c_str()); + loading_lbl.align(Align::OutTopMid.into(), 0, 0); let mut loading_style = Style::default(); loading_style.set_text_color(Color::from_rgb((0, 0, 0))); - loading_lbl.add_style(Part::Main, &mut loading_style)?; + loading_lbl.add_style(loading_style.into_raw(), Part::Main.into()); let mut i = 0; let clock = Clock::default(); 'running: loop { if i > 100 { i = 0; - lvgl::event_send(&mut bar, Event::Clicked)?; + lvgl::event_send(&mut bar, Event::Clicked); } - bar.set_value(i, AnimationState::ON)?; + bar.set_value(i, AnimationState::ON.into()); i += 1; lvgl::task_handler(); diff --git a/examples/sdl.rs b/examples/sdl.rs index 1c14f1a6..cdf447d7 100644 --- a/examples/sdl.rs +++ b/examples/sdl.rs @@ -8,9 +8,10 @@ use lvgl::input_device::InputDriver; use lvgl::lv_drv_disp_sdl; use lvgl::lv_drv_input_pointer_sdl; use lvgl::style::Style; +use lvgl::widgets::Widget; use lvgl::widgets::{Btn, Label}; use lvgl::LvResult; -use lvgl::{Align, Color, DrawBuffer, Part, Widget}; +use lvgl::{Align, Color, DrawBuffer, Part}; use std::thread::sleep; use std::time::Duration; use std::time::Instant; @@ -28,13 +29,13 @@ fn main() -> LvResult<()> { let mut screen_style = Style::default(); screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); - screen.add_style(Part::Main, &mut screen_style); + screen.add_style(screen_style.into_raw(), Part::Main.into()); // Create the button let mut button = Btn::create(&mut screen)?; - button.set_align(Align::LeftMid, 30, 0); + button.align(Align::LeftMid.into(), 30, 0); button.set_size(180, 80); let mut btn_lbl = Label::create(&mut button)?; - btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str())?; + btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str()); let mut btn_state = false; button.on_event(|_btn, event| { @@ -42,10 +43,10 @@ fn main() -> LvResult<()> { 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(); + btn_lbl.set_text(nt.as_c_str()); } else { let nt = CString::new("Clicked!").unwrap(); - btn_lbl.set_text(nt.as_c_str()).unwrap(); + btn_lbl.set_text(nt.as_c_str()); } btn_state = !btn_state; } diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 432f87cf..ca52f718 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -8,7 +8,7 @@ use quote::{format_ident, ToTokens}; use regex::Regex; use std::collections::HashMap; use std::error::Error; -use syn::{FnArg, ForeignItem, ForeignItemFn, Item, ReturnType}; +use syn::{parse_str, FnArg, ForeignItem, ForeignItemFn, Item, ReturnType, TypePath}; type CGResult = Result>; @@ -17,10 +17,12 @@ const LIB_PREFIX: &str = "lv_"; lazy_static! { static ref TYPE_MAPPINGS: HashMap<&'static str, &'static str> = [ ("u16", "u16"), + ("u32", "u32"), ("i32", "i32"), + ("i16", "i16"), ("u8", "u8"), + ("i8", "i8"), ("bool", "bool"), - ("* const cty :: c_char", "_"), ] .iter() .cloned() @@ -56,20 +58,28 @@ impl Rusty for LvWidget { type Parent = (); fn code(&self, _parent: &Self::Parent) -> WrapperResult { - // We don't generate for the generic Obj - if self.name.as_str().eq("obj") { - return Err(WrapperError::Skip); - } - let widget_name = format_ident!("{}", self.pascal_name()); let methods: Vec = self.methods.iter().flat_map(|m| m.code(self)).collect(); - Ok(quote! { - define_object!(#widget_name); + if self.name.as_str().eq("obj") { + Ok(quote! { + pub trait Widget<'a>: NativeObject + Sized + 'a { + type SpecialEvent; + type Part: Into; - impl<'a> #widget_name<'a> { - #(#methods)* - } - }) + unsafe fn from_raw(raw_pointer: core::ptr::NonNull) -> Option; + + #(#methods)* + } + }) + } else { + Ok(quote! { + define_object!(#widget_name); + + impl<'a> #widget_name<'a> { + #(#methods)* + } + }) + } } } @@ -104,7 +114,7 @@ impl Rusty for LvFunc { let original_func_name = format_ident!("{}", self.name.as_str()); // generate constructor - if new_name.as_str().eq("create") { + if new_name.as_str().eq("create") && parent.name != "obj" { return Ok(quote! { pub fn create(parent: &mut impl crate::NativeObject) -> crate::LvResult { @@ -113,7 +123,7 @@ impl Rusty for LvFunc { parent.raw().as_mut(), ); if let Some(raw) = core::ptr::NonNull::new(ptr) { - let core = ::from_raw(raw).unwrap(); + let core = ::from_raw(raw).unwrap(); Ok(Self { core }) } else { Err(crate::LvError::InvalidReference) @@ -136,15 +146,14 @@ impl Rusty for LvFunc { // function returns something _ => { let return_value: &LvType = self.ret.as_ref().unwrap(); - match return_value.literal_name.as_str() { - "bool" => quote!(bool), - "u32" => quote!(u32), - "i32" => quote!(i32), - "u16" => quote!(u16), - "i16" => quote!(i16), - "u8" => quote!(u8), - "i8" => quote!(i8), - _ => return Err(WrapperError::Skip) + if !return_value.is_pointer() { + parse_str(&return_value.literal_name).expect(&format!( + "Cannot parse {} as type", + return_value.literal_name + )) + } else { + println!("Return value is pointer ({})", return_value.literal_name); + return Err(WrapperError::Skip); } } }; @@ -158,32 +167,56 @@ impl Rusty for LvFunc { // // - Iif the first argument (of the C function) is const then we require a &self immutable reference, otherwise an &mut self reference // - The arguments will be appended to the accumulator (args_accumulator) as they are generated in the closure - let args_decl = self + let args_decl = + self.args + .iter() + .enumerate() + .fold(quote!(), |args_accumulator, (arg_idx, arg)| { + let next_arg = if arg_idx == 0 { + if arg.get_type().is_const() { + quote!(&self) + } else { + quote!(&mut self) + } + } else { + arg.code(self).unwrap() + }; + + // If the accummulator is empty then we call quote! only with the next_arg content + if args_accumulator.is_empty() { + quote! {#next_arg} + } + // Otherwise we append next_arg at the end of the accumulator + else { + quote! {#args_accumulator, #next_arg} + } + }); + + let args_preprocessing = self .args .iter() .enumerate() - .fold(quote!(), |args_accumulator, (arg_idx, arg)| { - let next_arg = if arg_idx == 0 { - if arg.get_type().is_const() { - quote!(&self) - } else { - quote!(&mut self) - } + .fold(quote!(), |args, (i, arg)| { + // if first arg is `const`, then it should be immutable + let next_arg = if i == 0 { + quote!() } else { - arg.code(self).unwrap() + let var = arg.get_preprocessing(); + quote!(#var) }; - - // If the accummulator is empty then we call quote! only with the next_arg content - if args_accumulator.is_empty() { - quote! {#next_arg} - } - // Otherwise we append next_arg at the end of the accumulator - else { - quote! {#args_accumulator, #next_arg} + if args.is_empty() { + quote! { + #next_arg + } + } else { + quote! { + #args + #next_arg + } } }); - let args_processing = self + let args_postprocessing = self .args .iter() .enumerate() @@ -192,7 +225,7 @@ impl Rusty for LvFunc { let next_arg = if i == 0 { quote!() } else { - let var = arg.get_processing(); + let var = arg.get_postprocessing(); quote!(#var) }; if args.is_empty() { @@ -212,27 +245,37 @@ impl Rusty for LvFunc { // - The first argument will be always self.core.raw().as_mut() (see quote! when arg_idx == 0), it's most likely a pointer to lv_obj_t // TODO: When handling getters this should be self.raw().as_ptr() instead, this also requires updating args_decl // - The arguments will be appended to the accumulator (args_accumulator) as they are generated in the closure - let ffi_args = self - .args - .iter() - .enumerate() - .fold(quote!(), |args_accumulator, (arg_idx, arg)| { - let next_arg = if arg_idx == 0 { - quote!(self.core.raw().as_mut()) - } else { - let var = arg.get_value_usage(); - quote!(#var) - }; + let ffi_args = + self.args + .iter() + .enumerate() + .fold(quote!(), |args_accumulator, (arg_idx, arg)| { + let next_arg = if arg_idx == 0 { + if parent.name == "obj" { + quote!(self.raw().as_mut()) + } else { + quote!(self.core.raw().as_mut()) + } + } else if arg.typ.is_mut_native_object() { + let var = arg.get_value_usage(); + quote! {#var.raw().as_mut()} + }else if arg.typ.is_const_native_object() { + let var = arg.get_value_usage(); + quote! {#var.raw().as_ref()} + } else { + let var = arg.get_value_usage(); + quote!(#var) + }; - // If the accummulator is empty then we call quote! only with the next_arg content - if args_accumulator.is_empty() { - quote! {#next_arg} - } - // Otherwise we append next_arg at the end of the accumulator - else { - quote! {#args_accumulator, #next_arg} - } - }); + // If the accummulator is empty then we call quote! only with the next_arg content + if args_accumulator.is_empty() { + quote! {#next_arg} + } + // Otherwise we append next_arg at the end of the accumulator + else { + quote! {#args_accumulator, #next_arg} + } + }); // NOTE: When the function returns something we can 'avoid' placing an Ok() at the end. let explicit_ok = if return_type.is_empty() { @@ -243,21 +286,34 @@ impl Rusty for LvFunc { // Append a semicolon at the end of the unsafe code only if there's no return value. // Otherwise we should remove it - let optional_semicolon= match self.ret { + let optional_semicolon = match self.ret { None => quote!(;), - _ => quote!() + _ => quote!(), }; - - Ok(quote! { - pub fn #func_name(#args_decl) -> #return_type { - #args_processing - unsafe { - lvgl_sys::#original_func_name(#ffi_args)#optional_semicolon + if parent.name == "obj" { + // pub keyword cannot be used in traits + Ok(quote! { + fn #func_name(#args_decl) -> #return_type { + unsafe { + #args_preprocessing + lvgl_sys::#original_func_name(#ffi_args)#optional_semicolon + #args_postprocessing + #explicit_ok + } } - - #explicit_ok - } - }) + }) + } else { + Ok(quote! { + pub fn #func_name(#args_decl) -> #return_type { + unsafe { + #args_preprocessing + lvgl_sys::#original_func_name(#ffi_args)#optional_semicolon + #args_postprocessing + #explicit_ok + } + } + }) + } } } @@ -310,20 +366,46 @@ impl LvArg { .unwrap_or_else(|_| format_ident!("r#{}", self.name.as_str())) } - pub fn get_processing(&self) -> TokenStream { + pub fn get_preprocessing(&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! {} + if self.get_type().is_mut_str() { + // Convert CString to *mut i8 + let name = format_ident!("{}", &self.name); + let name_raw = format_ident!("{}_raw", &self.name); + quote! { + let #name_raw = #name.clone().into_raw(); + } + } else { + quote! {} + } + } + + pub fn get_postprocessing(&self) -> TokenStream { + if self.get_type().is_mut_str() { + // Convert *mut i8 back to CString + let name = format_ident!("{}", &self.name); + let name_raw = format_ident!("{}_raw", &self.name); + quote! { + *#name = cstr_core::CString::from_raw(#name_raw); + } + } else { + quote! {} + } } pub fn get_value_usage(&self) -> TokenStream { let ident = self.get_name_ident(); - if self.typ.is_str() { + if self.typ.is_const_str() { quote! { #ident.as_ptr() } + } else if self.typ.is_mut_str() { + let ident_raw = format_ident!("{}_raw", &ident); + quote! { + #ident_raw + } } else { quote! { #ident @@ -373,8 +455,30 @@ impl LvType { self.literal_name.starts_with("const ") } - pub fn is_str(&self) -> bool { - self.literal_name.ends_with("* const cty :: c_char") + pub fn is_const_str(&self) -> bool { + self.literal_name == "* const cty :: c_char" + } + + pub fn is_mut_str(&self) -> bool { + self.literal_name == "* mut cty :: c_char" + } + + pub fn is_const_native_object(&self) -> bool { + self.literal_name == "* const lv_obj_t" || + self.literal_name == "* const _lv_obj_t" + } + + pub fn is_mut_native_object(&self) -> bool { + self.literal_name == "* mut lv_obj_t" || + self.literal_name == "* mut _lv_obj_t" + } + + pub fn is_pointer(&self) -> bool { + self.literal_name.starts_with('*') + } + + pub fn is_array(&self) -> bool { + self.literal_name.starts_with("* mut *") } } @@ -382,23 +486,36 @@ impl Rusty for LvType { type Parent = LvArg; fn code(&self, _parent: &Self::Parent) -> WrapperResult { - match TYPE_MAPPINGS.get(self.literal_name.as_str()) { - Some(name) => { - let val = if self.is_str() { - quote!(&cstr_core::CStr) - } else if self.literal_name.contains("lv_") { - let ident = format_ident!("{}", name); - quote!(&#ident) - } else { - let ident = format_ident!("{}", name); - quote!(#ident) - }; - Ok(quote! { - #val - }) + let val = if self.is_const_str() { + quote!(&cstr_core::CStr) + } else if self.is_mut_str() { + quote!(&mut cstr_core::CString) + }else if self.is_const_native_object() { + quote!(&impl NativeObject) + } else if self.is_mut_native_object() { + quote!(&mut impl NativeObject) + } else if self.is_array() { + println!("Array as argument ({})", self.literal_name); + return Err(WrapperError::Skip); + } else { + let literal_name = self.literal_name.as_str(); + let raw_name = literal_name.replace("* const ", "").replace("* mut ", ""); + if raw_name == "cty :: c_void" { + println!("Void pointer as argument ({literal_name})"); + return Err(WrapperError::Skip); } - None => Err(WrapperError::Skip), - } + let ty: TypePath = + parse_str(&raw_name).expect(&format!("Cannot parse {raw_name} to a type")); + if self.literal_name.starts_with("* mut") { + quote!(&mut #ty) + } else if self.literal_name.starts_with("*") { + quote!(&#ty) + } else { + quote!(#ty) + } + }; + + Ok(val) } } @@ -636,6 +753,41 @@ mod test { assert_eq!(code.to_string(), expected_code.to_string()); } + #[test] + fn generate_method_wrapper_for_mut_str_types_as_argument() { + let bindgen_code = quote! { + extern "C" { + pub fn lv_dropdown_get_selected_str(obj: *const lv_obj_t, buf: *mut cty::c_char, buf_size: u32); + } + }; + let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap(); + + let dropdown_get_selected_str = cg.get(0).unwrap().clone(); + let parent_widget = LvWidget { + name: "dropdown".to_string(), + methods: vec![], + }; + + let code = dropdown_get_selected_str.code(&parent_widget).unwrap(); + let expected_code = quote! { + + pub fn get_selected_str(&mut self, buf: &mut cstr_core::CString, buf_size:u32) -> () { + unsafe { + let buf_raw = buf.clone().into_raw(); + lvgl_sys::lv_dropdown_get_selected_str( + self.core.raw().as_mut(), + buf_raw, + buf_size + ); + *buf = cstr_core::CString::from_raw(buf_raw); + } + } + + }; + + assert_eq!(code.to_string(), expected_code.to_string()); + } + #[test] fn generate_method_wrapper_for_void_return() { let bindgen_code = quote! { @@ -665,6 +817,40 @@ mod test { } } }; + assert_eq!(code.to_string(), expected_code.to_string()); + } + + #[test] + fn generate_method_wrapper_with_mut_obj_parameter() { + let bindgen_code = quote! { + extern "C" { + pub fn lv_arc_rotate_obj_to_angle( + obj: *const lv_obj_t, + obj_to_rotate: *mut lv_obj_t, + r_offset: lv_coord_t, + ); + } + }; + let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap(); + + let arc_rotate_obj_to_angle = cg.get(0).unwrap().clone(); + let parent_widget = LvWidget { + name: "arc".to_string(), + methods: vec![], + }; + + let code = arc_rotate_obj_to_angle.code(&parent_widget).unwrap(); + let expected_code = quote! { + pub fn rotate_obj_to_angle(&mut self, obj_to_rotate: &mut impl NativeObject, r_offset: lv_coord_t) -> () { + unsafe { + lvgl_sys::lv_arc_rotate_obj_to_angle( + self.core.raw().as_mut(), + obj_to_rotate.raw().as_mut(), + r_offset + ); + } + } + }; assert_eq!(code.to_string(), expected_code.to_string()); } @@ -777,7 +963,7 @@ mod test { parent.raw().as_mut(), ); if let Some(raw) = core::ptr::NonNull::new(ptr) { - let core = ::from_raw(raw).unwrap(); + let core = ::from_raw(raw).unwrap(); Ok(Self { core }) } else { Err(crate::LvError::InvalidReference) diff --git a/lvgl/build.rs b/lvgl/build.rs index 0630c6a3..bb429ab2 100644 --- a/lvgl/build.rs +++ b/lvgl/build.rs @@ -20,6 +20,7 @@ fn main() { .collect(); let code = quote! { + use lvgl_sys::*; #(#widgets_impl)* }; diff --git a/lvgl/src/functions.rs b/lvgl/src/functions.rs index e2a622c5..ff2a20a2 100644 --- a/lvgl/src/functions.rs +++ b/lvgl/src/functions.rs @@ -1,6 +1,7 @@ use crate::display::{Display, DisplayDriver}; use crate::input_device::InputDriver; -use crate::{Event, LvError, LvResult, Obj, Widget}; +use crate::widgets::Widget; +use crate::{Event, LvError, LvResult, Obj}; use core::ptr::NonNull; #[cfg(not(feature = "rust_timer"))] use core::time::Duration; @@ -69,9 +70,9 @@ pub fn task_handler() { /// Directly send an event to a specific widget. #[inline] -pub fn event_send Widget<'a>>( +pub fn event_send<'a, W: Widget<'a>>( obj: &mut W, - event: Event<>::SpecialEvent>, + event: Event<>::SpecialEvent>, ) { unsafe { lvgl_sys::lv_event_send(obj.raw().as_mut(), event.into(), ptr::null_mut()); diff --git a/lvgl/src/lv_core/obj.rs b/lvgl/src/lv_core/obj.rs index 433ca537..2ab46aa5 100644 --- a/lvgl/src/lv_core/obj.rs +++ b/lvgl/src/lv_core/obj.rs @@ -5,8 +5,8 @@ //! are special in that they do not have a parent object but do still implement //! `NativeObject`. -use crate::lv_core::style::Style; -use crate::{Align, LvError, LvResult}; +use crate::widgets::Widget; +use crate::{LvError, LvResult}; use core::fmt::{self, Debug}; use core::marker::PhantomData; use core::ptr::{self, NonNull}; @@ -78,7 +78,7 @@ impl NativeObject for Obj<'_> { } /// A wrapper for all LVGL common operations on generic objects. -pub trait Widget<'a>: NativeObject + Sized + 'a { +/*pub trait Widget<'a>: NativeObject + Sized + 'a { type SpecialEvent; type Part: Into; @@ -149,7 +149,7 @@ pub trait Widget<'a>: NativeObject + Sized + 'a { ); } } -} +}*/ impl<'a> Widget<'a> for Obj<'a> { type SpecialEvent = u32; @@ -185,7 +185,7 @@ macro_rules! define_object { impl<'a> $item<'a> { pub fn on_event(&mut self, f: F) -> $crate::LvResult<()> where - F: FnMut(Self, $crate::support::Event<>::SpecialEvent>), + F: FnMut(Self, $crate::support::Event<>::SpecialEvent>), { use $crate::NativeObject; unsafe { @@ -210,7 +210,7 @@ macro_rules! define_object { } } - impl<'a> $crate::Widget<'a> for $item<'a> { + impl<'a> Widget<'a> for $item<'a> { type SpecialEvent = $event_type; type Part = $part_type; diff --git a/lvgl/src/lv_core/screen.rs b/lvgl/src/lv_core/screen.rs index ef87065b..4bc94986 100644 --- a/lvgl/src/lv_core/screen.rs +++ b/lvgl/src/lv_core/screen.rs @@ -1,4 +1,4 @@ -use crate::{LvError, LvResult, NativeObject, Obj, Part, Widget}; +use crate::{widgets::Widget, LvError, LvResult, NativeObject, Obj, Part}; /// An LVGL screen. #[derive(Debug)] diff --git a/lvgl/src/lv_core/style.rs b/lvgl/src/lv_core/style.rs index f37a34a4..9bb639fc 100644 --- a/lvgl/src/lv_core/style.rs +++ b/lvgl/src/lv_core/style.rs @@ -3,13 +3,13 @@ //! Objects in LVGL can have associated styling information. After a `Style` is //! created and configured, it can be added to any object or widget: //! ``` -//! use lvgl::{Color, Widget}; +//! use lvgl::{Color, Part}; //! use lvgl::style::Style; //! //! let mut my_style = Style::default(); //! my_style.set_text_color(Color::from_rgb((0, 0, 0))); //! -//! //my_widget.add_style(Part::Main, &mut my_style).unwrap(); +//! // my_widget.add_style(my_style.into_raw(), Part::Main.into()); //! // ... //! ``` //! All methods on the `Style` type directly lower to their C LVGL @@ -33,6 +33,12 @@ pub struct Style { pub(crate) raw: Box, } +impl Style { + pub fn into_raw(self) -> &'static mut lvgl_sys::lv_style_t { + unsafe { self.raw.into_raw().as_mut().unwrap() } + } +} + impl Debug for Style { // TODO: Decode and dump style values fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/lvgl/src/misc/anim.rs b/lvgl/src/misc/anim.rs index 206e9a93..4f26d6dc 100644 --- a/lvgl/src/misc/anim.rs +++ b/lvgl/src/misc/anim.rs @@ -1,4 +1,4 @@ -use crate::{Box, LvResult, Obj, Widget}; +use crate::{widgets::Widget, Box, LvResult, Obj}; use core::{ mem::{self, MaybeUninit}, num::TryFromIntError, @@ -115,7 +115,7 @@ where // yes, we have to do it this way. Casting `obj` directly to `&mut Obj` segfaults let obj = (*(obj as *mut T)).raw(); if !anim.as_ref().user_data.is_null() { - let callback = &mut *(obj.as_ref().user_data as *mut F); + let callback = &mut *(anim.as_ref().user_data as *mut F); let mut obj_nondrop = Obj::from_raw(obj).unwrap(); callback(&mut obj_nondrop, val); mem::forget(obj_nondrop) diff --git a/lvgl/src/support.rs b/lvgl/src/support.rs index a85cba6a..3f1366b7 100644 --- a/lvgl/src/support.rs +++ b/lvgl/src/support.rs @@ -1,5 +1,4 @@ -use crate::display::DisplayError; -use crate::Widget; +use crate::{display::DisplayError, widgets::Widget}; use core::convert::{TryFrom, TryInto}; #[cfg(feature = "nightly")] use core::error::Error; diff --git a/lvgl/src/widgets/bar.rs b/lvgl/src/widgets/bar.rs index be7ae2ff..f34b4b59 100644 --- a/lvgl/src/widgets/bar.rs +++ b/lvgl/src/widgets/bar.rs @@ -3,20 +3,20 @@ use crate::widgets::Bar; use crate::NativeObject; impl Bar<'_> { - /// Set minimum and the maximum values of the bar - //pub fn set_range(&mut self, min: i16, max: i16) -> LvResult<()> { + /*/// 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: i32, anim: AnimationState) { // unsafe { - // lvgl_sys::lv_bar_set_range(self.core.raw()?.as_mut(), min, max); + // lvgl_sys::lv_bar_set_value(self.core.raw().as_mut(), value, anim.into()); // } - // Ok(()) - //} - - /// Set a new value on the bar - pub fn set_value(&mut self, value: i32, anim: AnimationState) { - unsafe { - lvgl_sys::lv_bar_set_value(self.core.raw().as_mut(), value, anim.into()); - } - } + //}*/ } /* /// The different parts, of a bar object. diff --git a/lvgl/src/widgets/keyboard.rs b/lvgl/src/widgets/keyboard.rs index ebdaf050..8afbc7bc 100644 --- a/lvgl/src/widgets/keyboard.rs +++ b/lvgl/src/widgets/keyboard.rs @@ -1,14 +1,4 @@ use crate::widgets::{Keyboard, Textarea}; use crate::NativeObject; -impl Keyboard<'_> { - /// Associates a given `Textarea` to the keyboard. - pub fn set_textarea(&mut self, textarea: &mut Textarea) { - unsafe { - lvgl_sys::lv_keyboard_set_textarea( - self.raw().as_mut() as *mut lvgl_sys::lv_obj_t, - textarea.raw().as_mut() as *mut lvgl_sys::lv_obj_t, - ) - } - } -} +impl Keyboard<'_> {} diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index fa5142fd..d1b71451 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -32,13 +32,13 @@ mod alloc_imp { } impl Label<'_> { - pub fn set_long_mode(&mut self, long_mode: LabelLongMode) { + /*pub fn set_long_mode(&mut self, long_mode: LabelLongMode) { unsafe { lvgl_sys::lv_label_set_long_mode(self.raw().as_mut(), long_mode.into()); } - } + }*/ - pub fn get_long_mode(&self) -> u8 { + /*pub fn get_long_mode(&self) -> u8 { unsafe { lvgl_sys::lv_label_get_long_mode(self.raw().as_ref()) } - } + }*/ } diff --git a/lvgl/src/widgets/table.rs b/lvgl/src/widgets/table.rs index cda81588..9cdbb33d 100644 --- a/lvgl/src/widgets/table.rs +++ b/lvgl/src/widgets/table.rs @@ -3,13 +3,7 @@ use crate::widgets::Table; use core::mem::MaybeUninit; impl Table<'_> { - /// Sets the column width. Row height cannot be set manually and is - /// calculated by LVGL based on styling parameters. - pub fn set_col_width(&mut self, column: u16, width: i16) { - unsafe { lvgl_sys::lv_table_set_col_width(self.core.raw().as_ptr(), column, width) } - } - - /// Returns the selected cell as a tuple of (row, column). + /*/// Returns the selected cell as a tuple of (row, column). pub fn get_selected_cell(&self) -> (u16, u16) { let mut row = MaybeUninit::::uninit(); let mut col = MaybeUninit::::uninit(); @@ -22,5 +16,5 @@ impl Table<'_> { // The values get initialised by LVGL (row.assume_init(), col.assume_init()) } - } + }*/ }