From 91f9865a313a927f23228ef729bec2f597224798 Mon Sep 17 00:00:00 2001 From: C47D Date: Sun, 12 Jan 2025 15:27:03 -0600 Subject: [PATCH 01/11] codegen: Handle return values of u[8,16,32] and boolean type --- lvgl-codegen/src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index ca97a8c6..c9b90d29 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -129,10 +129,47 @@ impl Rusty for LvFunc { }); } - // We don't deal with methods that return types yet - if self.ret.is_some() { - return Err(WrapperError::Skip); - } + // Handle return values + let mut has_return_value = false; + let return_type = match self.ret { + // function returns void + None => quote!(()), + // function returns something + _ => { + let literal_name = self.ret.as_ref().unwrap().literal_name.clone(); + match literal_name { + _ if literal_name == "bool" => { + has_return_value = true; + quote!(bool) + } + _ if literal_name == "u32" => { + has_return_value = true; + quote!(u32) + } + _ if literal_name == "i32" => { + has_return_value = true; + quote!(i32) + } + _ if literal_name == "u16" => { + has_return_value = true; + quote!(u16) + } + _ if literal_name == "i16" => { + has_return_value = true; + quote!(i16) + } + _ if literal_name == "u8" => { + has_return_value = true; + quote!(u8) + } + _ if literal_name == "i8" => { + has_return_value = true; + quote!(i8) + } + _ => return Err(WrapperError::Skip) + } + } + }; // Make sure all arguments can be generated, skip the first arg (self)! for arg in self.args.iter().skip(1) { From ec74540d0662199fb8e4a722be09269d2f3f29bb Mon Sep 17 00:00:00 2001 From: C47D Date: Sun, 12 Jan 2025 15:27:46 -0600 Subject: [PATCH 02/11] codegen: Update wrapper creation to handle return values --- lvgl-codegen/src/lib.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index c9b90d29..7a62c50e 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -249,14 +249,29 @@ impl Rusty for LvFunc { } }); - // TODO: Handle methods that return types + // NOTE: When the function returns something we can 'avoid' placing an Ok() + // at the end. + let return_ok_at_the_end = if return_type.is_empty() { + quote!(Ok(())) + } else { + quote!() + }; + + // And we can also return from the unsafe block by removing the ; at the end + let implicit_return = if has_return_value { + quote!() + } else { + quote!(;) + }; + Ok(quote! { - pub fn #func_name(#args_decl) -> crate::LvResult<()> { + pub fn #func_name(#args_decl) -> #return_type { #args_processing unsafe { - lvgl_sys::#original_func_name(#args_call); + lvgl_sys::#original_func_name(#args_call)#implicit_return } - Ok(()) + + #return_ok_at_the_end } }) } From 4a505ba6ec84b8ff077726412a2b3a4d88fe6794 Mon Sep 17 00:00:00 2001 From: C47D Date: Sun, 12 Jan 2025 15:28:30 -0600 Subject: [PATCH 03/11] codegen: How to handle calls for getters? --- lvgl-codegen/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 7a62c50e..af839894 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -226,6 +226,8 @@ impl Rusty for LvFunc { } }); + // TODO Unsafe function for getters should be lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) ? + // Currently they are lvgl_sys :: lv_bar_get_value (self . core . raw () . as_mut ()) } let args_call = self .args .iter() From 4ef1cc6a70b5dd9b17f181c085d586cd634d6410 Mon Sep 17 00:00:00 2001 From: C47D Date: Sun, 12 Jan 2025 15:29:09 -0600 Subject: [PATCH 04/11] codegen: Update and handle tests for getters --- lvgl-codegen/src/lib.rs | 97 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index af839894..53782831 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -609,11 +609,10 @@ mod test { let code = arc_set_bg_end_angle.code(&arc_widget).unwrap(); let expected_code = quote! { - pub fn set_bg_end_angle(&mut self, end: u16) -> crate::LvResult<()> { + pub fn set_bg_end_angle(&mut self, end: u16) -> () { unsafe { lvgl_sys::lv_arc_set_bg_end_angle(self.core.raw().as_mut(), end); } - Ok(()) } }; @@ -641,14 +640,13 @@ mod test { let code = label_set_text.code(&parent_widget).unwrap(); let expected_code = quote! { - pub fn set_text(&mut self, text: &cstr_core::CStr) -> crate::LvResult<()> { + pub fn set_text(&mut self, text: &cstr_core::CStr) -> () { unsafe { lvgl_sys::lv_label_set_text( self.core.raw().as_mut(), text.as_ptr() ); } - Ok(()) } }; @@ -656,6 +654,97 @@ mod test { assert_eq!(code.to_string(), expected_code.to_string()); } + #[test] + fn generate_method_wrapper_for_void_return() { + let bindgen_code = quote! { + extern "C" { + #[doc = " Set a new text for a label. Memory will be allocated to store the text by the label."] + #[doc = " @param label pointer to a label object"] + #[doc = " @param text '\\0' terminated character string. NULL to refresh with the current text."] + pub fn lv_label_set_text(label: *mut lv_obj_t, text: *const cty::c_char); + } + }; + let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap(); + + let label_set_text = cg.get(0).unwrap().clone(); + let parent_widget = LvWidget { + name: "label".to_string(), + methods: vec![], + }; + + let code = label_set_text.code(&parent_widget).unwrap(); + let expected_code = quote! { + pub fn set_text(&mut self, text: &cstr_core::CStr) -> () { + unsafe { + lvgl_sys::lv_label_set_text( + self.core.raw().as_mut(), + text.as_ptr() + ); + } + } + }; + + assert_eq!(code.to_string(), expected_code.to_string()); + } + + #[test] + fn generate_method_wrapper_for_boolean_return() { + let bindgen_code = quote! { + extern "C" { + pub fn lv_label_get_recolor(label: *mut lv_obj_t) -> bool; + } + }; + let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap(); + + let label_get_recolor = cg.get(0).unwrap().clone(); + let parent_widget = LvWidget { + name: "label".to_string(), + methods: vec![], + }; + + let code = label_get_recolor.code(&parent_widget).unwrap(); + let expected_code = quote! { + pub fn get_recolor(&mut self) -> bool { + unsafe { + lvgl_sys::lv_label_get_recolor( + self.core.raw().as_mut() + ) + } + } + }; + + assert_eq!(code.to_string(), expected_code.to_string()); + } + + #[test] + fn generate_method_wrapper_for_uint32_return() { + let bindgen_code = quote! { + extern "C" { + pub fn lv_label_get_text_selection_start(label: *mut lv_obj_t) -> u32; + } + }; + let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap(); + + let label_get_text_selection_start = cg.get(0).unwrap().clone(); + let parent_widget = LvWidget { + name: "label".to_string(), + methods: vec![], + }; + + let code = label_get_text_selection_start.code(&parent_widget).unwrap(); + let expected_code = quote! { + pub fn get_text_selection_start(&mut self) -> u32 { + unsafe { + lvgl_sys::lv_label_get_text_selection_start( + self.core.raw().as_mut() + ) + } + } + }; + + assert_eq!(code.to_string(), expected_code.to_string()); + } + #[test] fn generate_basic_widget_code() { let arc_widget = LvWidget { From eb2c81af9fd00c207b2f73568f7714926fde786e Mon Sep 17 00:00:00 2001 From: C47D Date: Sun, 12 Jan 2025 15:30:42 -0600 Subject: [PATCH 05/11] Remove manual getters from custom code, now they're generated --- lvgl/src/widgets/arc.rs | 8 ++++---- lvgl/src/widgets/bar.rs | 8 ++++---- lvgl/src/widgets/label.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lvgl/src/widgets/arc.rs b/lvgl/src/widgets/arc.rs index 5fd94a0c..6bd1b69a 100644 --- a/lvgl/src/widgets/arc.rs +++ b/lvgl/src/widgets/arc.rs @@ -38,10 +38,10 @@ impl Arc<'_> { // Ok(()) // } - /// Gets the current value of the arc - pub fn get_value(&self) -> i32 { - unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } - } + // Gets the current value of the arc + // pub fn get_value(&self) -> i32 { + // unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } + // } } /* /// The different parts, of an arc object. diff --git a/lvgl/src/widgets/bar.rs b/lvgl/src/widgets/bar.rs index 4a50d6b1..5c0d627c 100644 --- a/lvgl/src/widgets/bar.rs +++ b/lvgl/src/widgets/bar.rs @@ -18,10 +18,10 @@ impl Bar<'_> { } } - /// Gets the current value of the bar - pub fn get_value(&self) -> i32 { - unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } - } + // Gets the current value of the bar + // pub fn get_value(&self) -> i32 { + // unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } + // } } /* /// The different parts, of a bar object. diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index 691ffb8d..fa5142fd 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -13,7 +13,7 @@ mod alloc_imp { // text.try_into().unwrap() let text_cstr = CString::new(text.as_ref()).unwrap(); let mut label = Label::new().unwrap(); - label.set_text(text_cstr.as_c_str()).unwrap(); + label.set_text(text_cstr.as_c_str()); label } } From 769e47d0ec712fe031b1113bdb4b2bba61f0986c Mon Sep 17 00:00:00 2001 From: C47D Date: Mon, 13 Jan 2025 19:15:47 -0600 Subject: [PATCH 06/11] codegen: Add comments and renaming variables for clarity --- lvgl-codegen/src/lib.rs | 66 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 53782831..a0f04e15 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -176,13 +176,16 @@ impl Rusty for LvFunc { arg.code(self)?; } + // Generate the arguments being passed into the Rust 'wrapper' + // + // - 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 .args .iter() .enumerate() - .fold(quote!(), |args, (i, arg)| { - // if first arg is `const`, then it should be immutable - let next_arg = if i == 0 { + .fold(quote!(), |args_accumulator, (arg_idx, arg)| { + let next_arg = if arg_idx == 0 { if arg.get_type().is_const() { quote!(&self) } else { @@ -191,14 +194,14 @@ impl Rusty for LvFunc { } else { arg.code(self).unwrap() }; - if args.is_empty() { - quote! { - #next_arg - } - } else { - quote! { - #args, #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} } }); @@ -226,41 +229,42 @@ impl Rusty for LvFunc { } }); - // TODO Unsafe function for getters should be lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) ? - // Currently they are lvgl_sys :: lv_bar_get_value (self . core . raw () . as_mut ()) } - let args_call = self + // Generate the arguments being passed into the FFI interface + // + // - 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, (i, arg)| { - // if first arg is `const`, then it should be immutable - let next_arg = if i == 0 { + .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) }; - if args.is_empty() { - quote! { - #next_arg - } - } else { - quote! { - #args, #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 return_ok_at_the_end = if return_type.is_empty() { + // NOTE: When the function returns something we can 'avoid' placing an Ok() at the end. + let explicit_ok = if return_type.is_empty() { quote!(Ok(())) } else { quote!() }; // And we can also return from the unsafe block by removing the ; at the end - let implicit_return = if has_return_value { + let optional_semicolon = if has_return_value { quote!() } else { quote!(;) @@ -270,10 +274,10 @@ impl Rusty for LvFunc { pub fn #func_name(#args_decl) -> #return_type { #args_processing unsafe { - lvgl_sys::#original_func_name(#args_call)#implicit_return + lvgl_sys::#original_func_name(#ffi_args)#optional_semicolon } - #return_ok_at_the_end + #explicit_ok } }) } From 355541e30eb7676f31908892e46429e3184a62a8 Mon Sep 17 00:00:00 2001 From: C47D Date: Tue, 14 Jan 2025 00:06:41 -0600 Subject: [PATCH 07/11] codegen: Improve? matching return types --- lvgl-codegen/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index a0f04e15..81e7eb86 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -137,32 +137,32 @@ impl Rusty for LvFunc { // function returns something _ => { let literal_name = self.ret.as_ref().unwrap().literal_name.clone(); - match literal_name { - _ if literal_name == "bool" => { + match literal_name.as_str() { + "bool" => { has_return_value = true; quote!(bool) } - _ if literal_name == "u32" => { + "u32" => { has_return_value = true; quote!(u32) } - _ if literal_name == "i32" => { + "i32" => { has_return_value = true; quote!(i32) } - _ if literal_name == "u16" => { + "u16" => { has_return_value = true; quote!(u16) } - _ if literal_name == "i16" => { + "i16" => { has_return_value = true; quote!(i16) } - _ if literal_name == "u8" => { + "u8" => { has_return_value = true; quote!(u8) } - _ if literal_name == "i8" => { + "i8" => { has_return_value = true; quote!(i8) } From 30be7036b83e648e2b336c3a7190ef2cefdda5b0 Mon Sep 17 00:00:00 2001 From: C47D Date: Tue, 14 Jan 2025 23:07:58 -0600 Subject: [PATCH 08/11] label: Comment out get_recolor interface --- lvgl/src/widgets/label.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index fa5142fd..c779160f 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -41,4 +41,8 @@ impl Label<'_> { pub fn get_long_mode(&self) -> u8 { unsafe { lvgl_sys::lv_label_get_long_mode(self.raw().as_ref()) } } + + // pub fn get_recolor(&self) -> bool { + // unsafe { lvgl_sys::lv_label_get_recolor(self.raw().as_ref()) } + // } } From 5ead5614dc8f4129c99c2b28abdada077ef0b0e1 Mon Sep 17 00:00:00 2001 From: C47D Date: Thu, 16 Jan 2025 00:56:54 -0600 Subject: [PATCH 09/11] codegen: Don't clone return value, we can reference it --- lvgl-codegen/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 81e7eb86..70efca67 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -136,8 +136,8 @@ impl Rusty for LvFunc { None => quote!(()), // function returns something _ => { - let literal_name = self.ret.as_ref().unwrap().literal_name.clone(); - match literal_name.as_str() { + let return_value: &LvType = self.ret.as_ref().unwrap(); + match return_value.literal_name.as_str() { "bool" => { has_return_value = true; quote!(bool) From 31e3ba1e4efd21eca35e829f221224f85e085bd2 Mon Sep 17 00:00:00 2001 From: C47D Date: Thu, 16 Jan 2025 01:18:58 -0600 Subject: [PATCH 10/11] codegen: Don't use extra variable, we can check self.ret to know if there's a return value --- lvgl-codegen/src/lib.rs | 46 +++++++++++------------------------------ 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/lvgl-codegen/src/lib.rs b/lvgl-codegen/src/lib.rs index 70efca67..432f87cf 100644 --- a/lvgl-codegen/src/lib.rs +++ b/lvgl-codegen/src/lib.rs @@ -130,7 +130,6 @@ impl Rusty for LvFunc { } // Handle return values - let mut has_return_value = false; let return_type = match self.ret { // function returns void None => quote!(()), @@ -138,34 +137,13 @@ impl Rusty for LvFunc { _ => { let return_value: &LvType = self.ret.as_ref().unwrap(); match return_value.literal_name.as_str() { - "bool" => { - has_return_value = true; - quote!(bool) - } - "u32" => { - has_return_value = true; - quote!(u32) - } - "i32" => { - has_return_value = true; - quote!(i32) - } - "u16" => { - has_return_value = true; - quote!(u16) - } - "i16" => { - has_return_value = true; - quote!(i16) - } - "u8" => { - has_return_value = true; - quote!(u8) - } - "i8" => { - has_return_value = true; - quote!(i8) - } + "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) } } @@ -263,11 +241,11 @@ impl Rusty for LvFunc { quote!() }; - // And we can also return from the unsafe block by removing the ; at the end - let optional_semicolon = if has_return_value { - quote!() - } else { - quote!(;) + // 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 { + None => quote!(;), + _ => quote!() }; Ok(quote! { From b21f94928cbb0ec57aca615a7e30cb0c2b83445b Mon Sep 17 00:00:00 2001 From: C47D Date: Thu, 16 Jan 2025 20:42:04 -0600 Subject: [PATCH 11/11] widgets: Remove commented out interfaces --- lvgl/src/widgets/arc.rs | 5 ----- lvgl/src/widgets/bar.rs | 5 ----- lvgl/src/widgets/label.rs | 4 ---- 3 files changed, 14 deletions(-) diff --git a/lvgl/src/widgets/arc.rs b/lvgl/src/widgets/arc.rs index 6bd1b69a..ea0a57dd 100644 --- a/lvgl/src/widgets/arc.rs +++ b/lvgl/src/widgets/arc.rs @@ -37,11 +37,6 @@ impl Arc<'_> { // } // Ok(()) // } - - // Gets the current value of the arc - // pub fn get_value(&self) -> i32 { - // unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } - // } } /* /// The different parts, of an arc object. diff --git a/lvgl/src/widgets/bar.rs b/lvgl/src/widgets/bar.rs index 5c0d627c..be7ae2ff 100644 --- a/lvgl/src/widgets/bar.rs +++ b/lvgl/src/widgets/bar.rs @@ -17,11 +17,6 @@ impl Bar<'_> { lvgl_sys::lv_bar_set_value(self.core.raw().as_mut(), value, anim.into()); } } - - // Gets the current value of the bar - // pub fn get_value(&self) -> i32 { - // unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) } - // } } /* /// The different parts, of a bar object. diff --git a/lvgl/src/widgets/label.rs b/lvgl/src/widgets/label.rs index c779160f..fa5142fd 100644 --- a/lvgl/src/widgets/label.rs +++ b/lvgl/src/widgets/label.rs @@ -41,8 +41,4 @@ impl Label<'_> { pub fn get_long_mode(&self) -> u8 { unsafe { lvgl_sys::lv_label_get_long_mode(self.raw().as_ref()) } } - - // pub fn get_recolor(&self) -> bool { - // unsafe { lvgl_sys::lv_label_get_recolor(self.raw().as_ref()) } - // } }