diff --git a/Kconfig b/Kconfig index 36a1fa1d5c..44ba6f003a 100644 --- a/Kconfig +++ b/Kconfig @@ -832,6 +832,23 @@ menu "LVGL configuration" bool "Enable the multi-touch gesture recognition feature" depends on LV_USE_FLOAT default n + + config LV_USE_EXT_DATA + bool "External data and destructor" + default n + help + Enable this option to activate external data and destructor functionality, + which assists in resource cleanup when objects are freed by either LVGL core + or applications. Currently supported features include: + - event + - object + - observer + - anim + - timer + - group + - display + - indev (input device) + - theme endmenu endmenu diff --git a/lv_conf_template.h b/lv_conf_template.h index f1913dc3df..d845a236e2 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -1400,6 +1400,9 @@ #define LV_QNX_BUF_COUNT 1 /**< 1 or 2 */ #endif +/** Enable or disable for external data and destructor function */ +#define LV_USE_EXT_DATA 0 + /*===================== * BUILD OPTIONS *======================*/ diff --git a/src/core/lv_group.c b/src/core/lv_group.c index d1cdb31766..f02e4fe25f 100644 --- a/src/core/lv_group.c +++ b/src/core/lv_group.c @@ -67,6 +67,10 @@ lv_group_t * lv_group_create(void) group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV; group->wrap = 1; group->user_data = NULL; +#if LV_USE_EXT_DATA + group->ext_data.free_cb = NULL; + group->ext_data.data = NULL; +#endif return group; } @@ -100,6 +104,12 @@ void lv_group_delete(lv_group_t * group) lv_ll_clear(&(group->obj_ll)); lv_ll_remove(group_ll_p, group); +#if LV_USE_EXT_DATA + if(group->ext_data.free_cb) { + group->ext_data.free_cb(group->ext_data.data); + group->ext_data.data = NULL; + } +#endif lv_free(group); } @@ -410,6 +420,20 @@ lv_group_t * lv_group_by_index(uint32_t index) return NULL; } + +#if LV_USE_EXT_DATA +void lv_group_set_external_data(lv_group_t * group, void * data, void (* free_cb)(void * data)) +{ + if(!group) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL group"); + return; + } + + group->ext_data.data = data; + group->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/core/lv_group.h b/src/core/lv_group.h index f092848e33..ea725538bb 100644 --- a/src/core/lv_group.h +++ b/src/core/lv_group.h @@ -237,6 +237,22 @@ uint32_t lv_group_get_count(void); */ lv_group_t * lv_group_by_index(uint32_t index); +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data and destructor callback to a group + * + * Associates custom user data with an LVGL group and specifies a destructor function + * that will be automatically invoked when the group is deleted to properly clean up + * the associated resources. + * + * @param group Pointer to a group + * @param data User-defined data pointer to associate with a group + * @param free_cb Callback function for cleaning up ext_data when group is deleted. + * Receives ext_data as parameter. NULL means no cleanup required. + */ +void lv_group_set_external_data(lv_group_t * group, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/core/lv_group_private.h b/src/core/lv_group_private.h index fe5f22b0e9..c9d7398190 100644 --- a/src/core/lv_group_private.h +++ b/src/core/lv_group_private.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_group.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -29,6 +33,9 @@ extern "C" { * They are NOT for laying out objects on a screen (try layouts for that). */ struct _lv_group_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/ lv_obj_t ** obj_focus; /**< The object in focus*/ diff --git a/src/core/lv_obj_class.c b/src/core/lv_obj_class.c index 0fb8ab5d23..061855f770 100644 --- a/src/core/lv_obj_class.c +++ b/src/core/lv_obj_class.c @@ -135,6 +135,13 @@ void lv_obj_class_init_obj(lv_obj_t * obj) void lv_obj_destruct(lv_obj_t * obj) { +#if LV_USE_EXT_DATA + if(obj->ext_data.free_cb) { + obj->ext_data.free_cb(obj->ext_data.data); + obj->ext_data.data = NULL; + } +#endif + if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj); if(obj->class_p->base_class) { @@ -170,6 +177,19 @@ bool lv_obj_is_group_def(lv_obj_t * obj) return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE; } +#if LV_USE_EXT_DATA +void lv_obj_set_external_data(lv_obj_t * obj, void * data, void (* free_cb)(void * data)) +{ + if(!obj) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL object"); + return; + } + + obj->ext_data.data = data; + obj->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ @@ -177,6 +197,12 @@ bool lv_obj_is_group_def(lv_obj_t * obj) static void lv_obj_construct(const lv_obj_class_t * class_p, lv_obj_t * obj) { LV_ASSERT_NULL(class_p->name); + +#if LV_USE_EXT_DATA + obj->ext_data.free_cb = NULL; + obj->ext_data.data = NULL; +#endif + if(obj->class_p->base_class) { const lv_obj_class_t * original_class_p = obj->class_p; diff --git a/src/core/lv_obj_class.h b/src/core/lv_obj_class.h index 4c3bdcb248..1c3a7b3761 100644 --- a/src/core/lv_obj_class.h +++ b/src/core/lv_obj_class.h @@ -61,6 +61,23 @@ bool lv_obj_is_editable(lv_obj_t * obj); bool lv_obj_is_group_def(lv_obj_t * obj); +#if LV_USE_EXT_DATA +/** + * @brief Associates an array of external data pointers with an LVGL object + * + * Associates custom user data with an LVGL object and specifies a destructor function + * that will be automatically invoked when the object is deleted to properly clean up + * the associated resources. + * + * @param obj Target LVGL object + * @param data User-defined data pointer to associate with a object + * @param free_cb Cleanup function called for each non-NULL data pointer during + * object deletion. Receives single data pointer as parameter. + * NULL means no automatic cleanup. + */ +void lv_obj_set_external_data(lv_obj_t * obj, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/core/lv_obj_private.h b/src/core/lv_obj_private.h index a28c87d0cd..b44fe39348 100644 --- a/src/core/lv_obj_private.h +++ b/src/core/lv_obj_private.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_obj.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -53,6 +57,9 @@ struct _lv_obj_spec_attr_t { }; struct _lv_obj_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif const lv_obj_class_t * class_p; lv_obj_t * parent; lv_obj_spec_attr_t * spec_attr; diff --git a/src/core/lv_observer.c b/src/core/lv_observer.c index 8ecfd15bfb..033deb3289 100644 --- a/src/core/lv_observer.c +++ b/src/core/lv_observer.c @@ -91,6 +91,19 @@ static void subject_set_string_free_user_data_event_cb(lv_event_t * e); * GLOBAL FUNCTIONS **********************/ +#if LV_USE_EXT_DATA +void lv_subject_set_external_data(lv_subject_t * subject, void * data, void (* free_cb)(void * data)) +{ + if(!subject) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL subject"); + return; + } + + subject->ext_data.data = data; + subject->ext_data.free_cb = free_cb; +} +#endif + void lv_subject_init_int(lv_subject_t * subject, int32_t value) { lv_memzero(subject, sizeof(lv_subject_t)); @@ -498,6 +511,13 @@ void lv_observer_remove(lv_observer_t * observer) observer->subject->notify_restart_query = 1; +#if LV_USE_EXT_DATA + if(observer->subject->ext_data.free_cb) { + observer->subject->ext_data.free_cb(observer->subject->ext_data.data); + observer->subject->ext_data.data = NULL; + } +#endif + lv_ll_remove(&(observer->subject->subs_ll), observer); if(observer->auto_free_user_data) { diff --git a/src/core/lv_observer.h b/src/core/lv_observer.h index 95538da672..c744fc11a9 100644 --- a/src/core/lv_observer.h +++ b/src/core/lv_observer.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_obj.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + #if LV_USE_OBSERVER /********************* @@ -56,6 +60,9 @@ typedef union { * The Subject (an observable value) */ struct _lv_subject_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif lv_ll_t subs_ll; /**< Subscribers */ lv_subject_value_t value; /**< Current value */ lv_subject_value_t prev_value; /**< Previous value */ @@ -79,6 +86,27 @@ typedef void (*lv_observer_cb_t)(lv_observer_t * observer, lv_subject_t * subjec * GLOBAL PROTOTYPES **********************/ +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data to an integer Subject with lifecycle management + * + * Associates arbitrary user-defined data with an LVGL observer and registers a destructor + * callback that will be automatically invoked when the observer is deleted. This enables: + * - Safe resource cleanup through the destructor mechanism + * - Contextual data storage for observer callbacks + * - Proper memory management for observer-related resources + * + * @param subject pointer to Subject + * @param data User-defined data pointer to associate + * @param free_cb Cleanup function called when: + * - Observer is explicitly deleted + * - Observed object is deleted + * - New data replaces current association + * NULL indicates no cleanup required + */ +void lv_subject_set_external_data(lv_subject_t * subject, void * data, void (* free_cb)(void * data)); +#endif + /** * Initialize an integer-type Subject. * @param subject pointer to Subject diff --git a/src/display/lv_display.c b/src/display/lv_display.c index f143aa7066..4cbfc3ec86 100644 --- a/src/display/lv_display.c +++ b/src/display/lv_display.c @@ -76,7 +76,10 @@ lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res) disp->antialiasing = LV_COLOR_DEPTH > 8 ? 1 : 0; disp->dpi = LV_DPI_DEF; disp->color_format = LV_COLOR_FORMAT_NATIVE; - +#if LV_USE_EXT_DATA + disp->ext_data.free_cb = NULL; + disp->ext_data.data = NULL; +#endif #if defined(LV_DRAW_SW_DRAW_UNIT_CNT) && (LV_DRAW_SW_DRAW_UNIT_CNT != 0) disp->tile_cnt = LV_DRAW_SW_DRAW_UNIT_CNT; @@ -227,6 +230,13 @@ void lv_display_delete(lv_display_t * disp) if(disp->layer_deinit) disp->layer_deinit(disp, disp->layer_head); lv_free(disp->layer_head); +#if LV_USE_EXT_DATA + if(disp->ext_data.free_cb) { + disp->ext_data.free_cb(disp->ext_data.data); + disp->ext_data.data = NULL; + } +#endif + lv_free(disp); if(was_default) lv_display_set_default(lv_ll_get_head(disp_ll_p)); @@ -1273,6 +1283,19 @@ int32_t lv_display_dpx(const lv_display_t * disp, int32_t n) return LV_DPX_CALC(lv_display_get_dpi(disp), n); } +#if LV_USE_EXT_DATA +void lv_display_set_external_data(lv_display_t * disp, void * data, void (* free_cb)(void * data)) +{ + if(!disp) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL display"); + return; + } + + disp->ext_data.data = data; + disp->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/display/lv_display.h b/src/display/lv_display.h index 945a322b56..6047e97b55 100644 --- a/src/display/lv_display.h +++ b/src/display/lv_display.h @@ -732,6 +732,22 @@ int32_t lv_dpx(int32_t n); */ int32_t lv_display_dpx(const lv_display_t * disp, int32_t n); +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data and destructor callback to a display + * + * Associates custom user data with an LVGL display and specifies a destructor function + * that will be automatically invoked when the display is deleted to properly clean up + * the associated resources. + * + * @param disp Pointer to a display + * @param data User-defined data pointer to associate with the display + * @param free_cb Callback function for cleaning up data when display is deleted. + * Receives data as parameter. NULL means no cleanup required. + */ +void lv_display_set_external_data(lv_display_t * disp, void * data, void (* free_cb)(void * data)); +#endif + #ifdef __cplusplus } /*extern "C"*/ #endif diff --git a/src/display/lv_display_private.h b/src/display/lv_display_private.h index c00e81a28d..de2bc1190b 100644 --- a/src/display/lv_display_private.h +++ b/src/display/lv_display_private.h @@ -22,6 +22,10 @@ extern "C" { #include "../debugging/sysmon/lv_sysmon_private.h" #endif +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -34,7 +38,9 @@ extern "C" { **********************/ struct _lv_display_t { - +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif /*--------------------- * Resolution *--------------------*/ diff --git a/src/indev/lv_indev.c b/src/indev/lv_indev.c index bfdd93bb8e..d8370e3763 100644 --- a/src/indev/lv_indev.c +++ b/src/indev/lv_indev.c @@ -142,6 +142,10 @@ lv_indev_t * lv_indev_create(void) indev->gesture_min_velocity = LV_INDEV_DEF_GESTURE_MIN_VELOCITY; indev->rotary_sensitivity = LV_INDEV_DEF_ROTARY_SENSITIVITY; indev->key_remap_cb = NULL; +#if LV_USE_EXT_DATA + indev->ext_data.free_cb = NULL; + indev->ext_data.data = NULL; +#endif #if LV_USE_GESTURE_RECOGNITION lv_indev_gesture_init(indev); @@ -163,6 +167,14 @@ void lv_indev_delete(lv_indev_t * indev) /*Remove the input device from the list*/ lv_ll_remove(indev_ll_head, indev); + +#if LV_USE_EXT_DATA + if(indev->ext_data.free_cb) { + indev->ext_data.free_cb(indev->ext_data.data); + indev->ext_data.data = NULL; + } +#endif + /*Free the memory of the input device*/ lv_free(indev); } @@ -685,6 +697,19 @@ void lv_indev_set_key_remap_cb(lv_indev_t * indev, lv_indev_key_remap_cb_t remap indev->key_remap_cb = remap_cb; } +#if LV_USE_EXT_DATA +void lv_indev_set_external_data(lv_indev_t * indev, void * data, void (* free_cb)(void * data)) +{ + if(!indev) { + LV_LOG_WARN("Can't attach external user data and free_cb callback to a NULL indev"); + return; + } + + indev->ext_data.data = data; + indev->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/indev/lv_indev.h b/src/indev/lv_indev.h index 2da2a837d8..a4ab173f69 100644 --- a/src/indev/lv_indev.h +++ b/src/indev/lv_indev.h @@ -452,6 +452,22 @@ lv_result_t lv_indev_send_event(lv_indev_t * indev, lv_event_code_t code, void * */ void lv_indev_set_key_remap_cb(lv_indev_t * indev, lv_indev_key_remap_cb_t remap_cb); +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data and destructor callback to an indev + * + * Associates custom user data with an LVGL indev and specifies a destructor function + * that will be automatically invoked when the indev is deleted to properly clean up + * the associated resources. + * + * @param indev Pointer to an indev + * @param data User-defined data pointer to associate with the indev + * @param free_cb Callback function for cleaning up ext_data when indev is deleted. + * Receives ext_data as parameter. NULL means no cleanup required. + */ +void lv_indev_set_external_data(lv_indev_t * indev, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/indev/lv_indev_private.h b/src/indev/lv_indev_private.h index dc98049082..bbc26727f5 100644 --- a/src/indev/lv_indev_private.h +++ b/src/indev/lv_indev_private.h @@ -18,6 +18,10 @@ extern "C" { #include "lv_indev_scroll.h" #include "lv_indev_gesture.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -28,6 +32,9 @@ extern "C" { **********************/ struct _lv_indev_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif /** Input device type*/ lv_indev_type_t type; diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 7b590ada23..cf93119285 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -4503,6 +4503,15 @@ #endif #endif +/** Enable or disable for external data and destructor function */ +#ifndef LV_USE_EXT_DATA + #ifdef CONFIG_LV_USE_EXT_DATA + #define LV_USE_EXT_DATA CONFIG_LV_USE_EXT_DATA + #else + #define LV_USE_EXT_DATA 0 + #endif +#endif + /*===================== * BUILD OPTIONS *======================*/ diff --git a/src/lvgl_private.h b/src/lvgl_private.h index 325290c223..19fe35d711 100644 --- a/src/lvgl_private.h +++ b/src/lvgl_private.h @@ -23,6 +23,13 @@ extern "C" { * TYPEDEFS **********************/ +#if LV_USE_EXT_DATA +typedef struct { + void * data; + void (* free_cb)(void * data); +} lv_ext_data_t; +#endif + /********************** * GLOBAL PROTOTYPES **********************/ diff --git a/src/misc/lv_anim.c b/src/misc/lv_anim.c index 1f651da132..9c20f3f0de 100644 --- a/src/misc/lv_anim.c +++ b/src/misc/lv_anim.c @@ -114,6 +114,10 @@ void lv_anim_init(lv_anim_t * a) a->repeat_cnt = 1; a->path_cb = lv_anim_path_linear; a->early_apply = 1; +#if LV_USE_EXT_DATA + a->ext_data.free_cb = NULL; + a->ext_data.data = NULL; +#endif } lv_anim_t * lv_anim_start(const lv_anim_t * a) @@ -555,6 +559,19 @@ void lv_anim_resume(lv_anim_t * a) a->run_round = state.anim_run_round; } +#if LV_USE_EXT_DATA +void lv_anim_set_external_data(lv_anim_t * anim, void * data, void (* free_cb)(void * data)) +{ + if(!a) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL animation"); + return; + } + + anim->ext_data.data = data; + anim->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ @@ -680,6 +697,12 @@ static void anim_completed_handler(lv_anim_t * a) /*Call the callback function at the end*/ if(a->completed_cb != NULL) a->completed_cb(a); if(a->deleted_cb != NULL) a->deleted_cb(a); +#if LV_USE_EXT_DATA + if(a->ext_data.free_cb) { + a->ext_data.free_cb(a->ext_data.data); + a->ext_data.data = NULL; + } +#endif lv_free(a); } /*If the animation is not deleted then restart it*/ @@ -797,6 +820,12 @@ static bool remove_concurrent_anims(const lv_anim_t * a_current) /*|| (a->custom_exec_cb && a->custom_exec_cb == a_current->custom_exec_cb)*/)) { lv_ll_remove(anim_ll_p, a); if(a->deleted_cb != NULL) a->deleted_cb(a); +#if LV_USE_EXT_DATA + if(a->ext_data.free_cb) { + a->ext_data.free_cb(a->ext_data.data); + a->ext_data.data = NULL; + } +#endif lv_free(a); /*Read by `anim_timer`. It need to know if a delete occurred in the linked list*/ anim_mark_list_change(); @@ -818,5 +847,11 @@ static void remove_anim(void * a) lv_anim_t * anim = a; lv_ll_remove(anim_ll_p, a); if(anim->deleted_cb != NULL) anim->deleted_cb(anim); +#if LV_USE_EXT_DATA + if(anim->ext_data.free_cb) { + anim->ext_data.free_cb(anim->ext_data.data); + anim->ext_data.data = NULL; + } +#endif lv_free(a); } diff --git a/src/misc/lv_anim.h b/src/misc/lv_anim.h index 9cd977106e..722da451b9 100644 --- a/src/misc/lv_anim.h +++ b/src/misc/lv_anim.h @@ -19,6 +19,10 @@ extern "C" { #include "lv_timer.h" #include "lv_ll.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -122,6 +126,9 @@ typedef struct { /** Describes an animation*/ struct _lv_anim_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif void * var; /**< Variable (Widget or other user-provided object) to animate */ lv_anim_exec_xcb_t exec_cb; /**< Function to execute to animate */ lv_anim_custom_exec_cb_t custom_exec_cb; /**< Function to execute to animate, @@ -555,6 +562,25 @@ int32_t lv_anim_path_step(const lv_anim_t * a); */ int32_t lv_anim_path_custom_bezier3(const lv_anim_t * a); +#if LV_USE_EXT_DATA +/** + * @brief Associates external user data with an animation instance + * + * Attaches arbitrary user-defined data to an LVGL animation object along with an optional + * destructor callback that will be automatically invoked when the animation completes + * or is deleted, enabling proper resource cleanup. + * + * @param anim Pointer to the animation object to configure + * @param data User-defined data pointer to associate + * @param free_cb Cleanup callback that receives ext_data when: + * - Animation completes naturally + * - Animation is deleted prematurely + * - New data replaces current association + * NULL indicates no cleanup required + */ +void lv_anim_set_external_data(lv_anim_t * anim, void * data, void (* free_cb)(void * data)); +#endif + /********************** * GLOBAL VARIABLES **********************/ diff --git a/src/misc/lv_event.c b/src/misc/lv_event.c index 34485becae..39c17ffd1e 100644 --- a/src/misc/lv_event.c +++ b/src/misc/lv_event.c @@ -54,6 +54,19 @@ static lv_event_dsc_t ** event_array_at(lv_event_list_t * list, uint32_t index); * GLOBAL FUNCTIONS **********************/ +#if LV_USE_EXT_DATA +void lv_event_desc_set_external_data(lv_event_dsc_t * dsc, void * data, void (* free_cb)(void * data)) +{ + if(!dsc) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL event descriptor"); + return; + } + + dsc->ext_data.data = data; + dsc->ext_data.free_cb = free_cb; +} +#endif + void lv_event_push(lv_event_t * e) { /*Build a simple linked list from the objects used in the events @@ -118,6 +131,9 @@ lv_result_t lv_event_send(lv_event_list_t * list, lv_event_t * e, bool preproces lv_event_code_t filter = dsc->filter & ~LV_EVENT_PREPROCESS; if(filter == LV_EVENT_ALL || filter == e->code) { e->user_data = dsc->user_data; +#if LV_USE_EXT_DATA + e->ext_data.data = dsc->ext_data.data; +#endif dsc->cb(e); if(e->stop_processing) break; @@ -149,6 +165,10 @@ lv_event_dsc_t * lv_event_add(lv_event_list_t * list, lv_event_cb_t cb, lv_event dsc->cb = cb; dsc->filter = filter; dsc->user_data = user_data; +#if LV_USE_EXT_DATA + dsc->ext_data.free_cb = NULL; + dsc->ext_data.data = NULL; +#endif if(event_array_size(list) == 0) { /*event list hasn't been initialized.*/ @@ -168,6 +188,12 @@ bool lv_event_remove_dsc(lv_event_list_t * list, lv_event_dsc_t * dsc) for(uint32_t i = 0; i < size; i++) { lv_event_dsc_t * event = *event_array_at(list, i); if(event == dsc) { +#if LV_USE_EXT_DATA + if(dsc->ext_data.free_cb) { + dsc->ext_data.free_cb(dsc->ext_data.data); + dsc->ext_data.data = NULL; + } +#endif event_mark_deleting(list, event); cleanup_event_list(list); return true; @@ -208,6 +234,12 @@ bool lv_event_remove(lv_event_list_t * list, uint32_t index) LV_ASSERT_NULL(list); lv_event_dsc_t * dsc = lv_event_get_dsc(list, index); if(dsc == NULL) return false; +#if LV_USE_EXT_DATA + if(dsc->ext_data.free_cb) { + dsc->ext_data.free_cb(dsc->ext_data.data); + dsc->ext_data.data = NULL; + } +#endif event_mark_deleting(list, dsc); cleanup_event_list(list); return true; @@ -217,8 +249,17 @@ void lv_event_remove_all(lv_event_list_t * list) { LV_ASSERT_NULL(list); const uint32_t size = event_array_size(list); - for(uint32_t i = 0; i < size; i++) + for(uint32_t i = 0; i < size; i++) { +#if LV_USE_EXT_DATA + lv_event_dsc_t * dsc = lv_event_get_dsc(list, i); + if(dsc && dsc->ext_data.free_cb) { + dsc->ext_data.free_cb(dsc->ext_data.data); + dsc->ext_data.data = NULL; + } +#endif event_mark_deleting(list, *event_array_at(list, i)); + } + cleanup_event_list(list); } diff --git a/src/misc/lv_event.h b/src/misc/lv_event.h index 16c0199720..be5d0f02c4 100644 --- a/src/misc/lv_event.h +++ b/src/misc/lv_event.h @@ -245,6 +245,19 @@ uint32_t lv_event_register_id(void); */ const char * lv_event_code_get_name(lv_event_code_t code); +#if LV_USE_EXT_DATA +/** + * Set external data and its destructor for an event descriptor. + * This allows associating custom data with an event callback that will be automatically cleaned up + * when the event descriptor is removed or destroyed. + * @param dsc pointer to an event descriptor (from lv_obj_add_event_cb) + * @param data pointer to the external data to associate with the event descriptor + * @param free_cb function pointer to a destructor that will be called to clean up the external data. + * The destructor will receive the data pointer as its parameter. + */ +void lv_event_desc_set_external_data(lv_event_dsc_t * dsc, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/misc/lv_event_private.h b/src/misc/lv_event_private.h index c380b4cb13..55ff270561 100644 --- a/src/misc/lv_event_private.h +++ b/src/misc/lv_event_private.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_event.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -25,6 +29,9 @@ extern "C" { **********************/ struct _lv_event_dsc_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif lv_event_cb_t cb; void * user_data; uint32_t filter; @@ -41,6 +48,9 @@ struct _lv_event_t { uint8_t stop_processing : 1; uint8_t stop_bubbling : 1; uint8_t stop_trickling : 1; +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif }; diff --git a/src/misc/lv_timer.c b/src/misc/lv_timer.c index 6b20713d27..eb29603b1a 100644 --- a/src/misc/lv_timer.c +++ b/src/misc/lv_timer.c @@ -177,6 +177,10 @@ lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * us new_timer->last_run = lv_tick_get(); new_timer->user_data = user_data; new_timer->auto_delete = true; +#if LV_USE_EXT_DATA + new_timer->ext_data.free_cb = NULL; + new_timer->ext_data.data = NULL; +#endif state.timer_created = true; @@ -196,6 +200,13 @@ void lv_timer_delete(lv_timer_t * timer) lv_ll_remove(timer_ll_p, timer); state.timer_deleted = true; +#if LV_USE_EXT_DATA + if(timer->ext_data.free_cb) { + timer->ext_data.free_cb(timer->ext_data.data); + timer->ext_data.data = NULL; + } +#endif + lv_free(timer); } @@ -301,6 +312,19 @@ bool lv_timer_get_paused(lv_timer_t * timer) return timer->paused; } +#if LV_USE_EXT_DATA +void lv_timer_set_external_data(lv_timer_t * timer, void * data, void (* free_cb)(void * data)) +{ + if(!timer) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL timer"); + return; + } + + timer->ext_data.data = data; + timer->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/misc/lv_timer.h b/src/misc/lv_timer.h index e7611ef0b0..76644f6120 100644 --- a/src/misc/lv_timer.h +++ b/src/misc/lv_timer.h @@ -196,6 +196,22 @@ void * lv_timer_get_user_data(lv_timer_t * timer); */ bool lv_timer_get_paused(lv_timer_t * timer); +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data and destructor callback to a timer object + * + * Associates custom user data with an LVGL timer and specifies a destructor function + * that will be automatically invoked when the timer is deleted to properly clean up + * the associated resources. + * + * @param timer Pointer to the timer object + * @param data User-defined data pointer to associate with the timer + * @param destructor Callback function for cleaning up ext_data when timer is deleted. + * Receives ext_data as parameter. NULL means no cleanup required. + */ +void lv_timer_set_external_data(lv_timer_t * timer, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/misc/lv_timer_private.h b/src/misc/lv_timer_private.h index 3f7f53a545..220641153a 100644 --- a/src/misc/lv_timer_private.h +++ b/src/misc/lv_timer_private.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_timer.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -28,6 +32,9 @@ extern "C" { * Descriptor of a lv_timer */ struct _lv_timer_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif uint32_t period; /**< How often the timer should run */ uint32_t last_run; /**< Last time the timer ran */ lv_timer_cb_t timer_cb; /**< Timer function */ diff --git a/src/themes/default/lv_theme_default.c b/src/themes/default/lv_theme_default.c index e0fed7f685..4ffb30b273 100644 --- a/src/themes/default/lv_theme_default.c +++ b/src/themes/default/lv_theme_default.c @@ -667,6 +667,10 @@ lv_theme_t * lv_theme_default_init(lv_display_t * disp, lv_color_t color_primary theme->base.font_large = font; theme->base.apply_cb = theme_apply; theme->base.flags = dark ? MODE_DARK : 0; +#if LV_USE_EXT_DATA + theme->base.ext_data.free_cb = NULL; + theme->base.ext_data.data = NULL; +#endif style_init(theme); @@ -711,6 +715,12 @@ void lv_theme_default_deinit(void) lv_style_reset(theme_styles + i); } } +#if LV_USE_EXT_DATA + if(theme->base.ext_data.free_cb) { + theme->base.ext_data.free_cb(theme->base.ext_data.data); + theme->base.ext_data.data = NULL; + } +#endif lv_free(theme_def); theme_def = NULL; } diff --git a/src/themes/lv_theme.c b/src/themes/lv_theme.c index 973760fb38..3a0372fb5d 100644 --- a/src/themes/lv_theme.c +++ b/src/themes/lv_theme.c @@ -113,6 +113,19 @@ lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj) return th ? th->color_secondary : lv_palette_main(LV_PALETTE_BLUE); } +#if LV_USE_EXT_DATA +void lv_theme_set_external_data(lv_theme_t * theme, void * data, void (* free_cb)(void * data)) +{ + if(!theme) { + LV_LOG_WARN("Can't attach external user data and destructor callback to a NULL theme"); + return; + } + + theme->ext_data.data = data; + theme->ext_data.free_cb = free_cb; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/themes/lv_theme.h b/src/themes/lv_theme.h index bd4f531748..dab88a4e4b 100644 --- a/src/themes/lv_theme.h +++ b/src/themes/lv_theme.h @@ -113,6 +113,22 @@ lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj); */ void lv_theme_delete(lv_theme_t * theme); +#if LV_USE_EXT_DATA +/** + * @brief Attaches external user data and destructor callback to the theme + * + * Associates custom user data with an LVGL theme and specifies a destructor function + * that will be automatically invoked when the theme is deleted to properly clean up + * the associated resources. + * + * @param theme Pointer to theme which callback should be set + * @param data User-defined data pointer to associate with the theme + * @param free_cb Callback function for cleaning up ext_data when theme is deleted. + * Receives ext_data as parameter. NULL means no cleanup required. + */ +void lv_theme_set_external_data(lv_theme_t * theme, void * data, void (* free_cb)(void * data)); +#endif + /********************** * MACROS **********************/ diff --git a/src/themes/lv_theme_private.h b/src/themes/lv_theme_private.h index f7bb24cd14..5421f30757 100644 --- a/src/themes/lv_theme_private.h +++ b/src/themes/lv_theme_private.h @@ -16,6 +16,10 @@ extern "C" { #include "lv_theme.h" +#if LV_USE_EXT_DATA +#include "../lvgl_private.h" +#endif + /********************* * DEFINES *********************/ @@ -25,6 +29,9 @@ extern "C" { **********************/ struct _lv_theme_t { +#if LV_USE_EXT_DATA + lv_ext_data_t ext_data; +#endif lv_theme_apply_cb_t apply_cb; lv_theme_t * parent; /**< Apply the current theme's style on top of this theme. */ void * user_data; diff --git a/src/themes/mono/lv_theme_mono.c b/src/themes/mono/lv_theme_mono.c index c3d9f387e2..0c539bf058 100644 --- a/src/themes/mono/lv_theme_mono.c +++ b/src/themes/mono/lv_theme_mono.c @@ -198,6 +198,10 @@ lv_theme_t * lv_theme_mono_init(lv_display_t * disp, bool dark_bg, const lv_font theme->base.font_normal = LV_FONT_DEFAULT; theme->base.font_large = LV_FONT_DEFAULT; theme->base.apply_cb = theme_apply; +#if LV_USE_EXT_DATA + theme->base.ext_data.free_cb = NULL; + theme->base.ext_data.data = NULL; +#endif style_init(theme, dark_bg, font); @@ -237,6 +241,12 @@ void lv_theme_mono_deinit(void) lv_style_reset(theme_styles + i); } } +#if LV_USE_EXT_DATA + if(theme->base.ext_data.free_cb) { + theme->base.ext_data.free_cb(theme->base.ext_data.data); + theme->base.ext_data.data = NULL; + } +#endif lv_free(theme_def); theme_def = NULL; } diff --git a/src/themes/simple/lv_theme_simple.c b/src/themes/simple/lv_theme_simple.c index 4fc6308b27..33266c0304 100644 --- a/src/themes/simple/lv_theme_simple.c +++ b/src/themes/simple/lv_theme_simple.c @@ -159,6 +159,10 @@ lv_theme_t * lv_theme_simple_init(lv_display_t * disp) theme->base.font_normal = LV_FONT_DEFAULT; theme->base.font_large = LV_FONT_DEFAULT; theme->base.apply_cb = theme_apply; +#if LV_USE_EXT_DATA + theme->base.ext_data.free_cb = NULL; + theme->base.ext_data.data = NULL; +#endif style_init(theme); @@ -198,6 +202,12 @@ void lv_theme_simple_deinit(void) lv_style_reset(theme_styles + i); } } +#if LV_USE_EXT_DATA + if(theme->base.ext_data.free_cb) { + theme->base.ext_data.free_cb(theme->base.ext_data.data); + theme->base.ext_data.data = NULL; + } +#endif lv_free(theme_def); theme_def = NULL; }