Skip to content

Commit 77bf4d4

Browse files
committed
Fix: Add safe LVGL object deletion to prevent crashes. Prevents LoadProhibited crashes from invalid LVGL object access.
1 parent 243a4e0 commit 77bf4d4

2 files changed

Lines changed: 64 additions & 20 deletions

File tree

src/applicationInternal/gui/guiBase.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,18 @@ void guis_doTabCreationForNavigateToLastActiveGUIofPreviousGUIlist() {
350350
// ------------------------------------------------------------------------------------------------------------
351351

352352
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en, bool send_tab_changed_event) {
353+
// Check if tabview is initialized before using it
354+
if (tabview == NULL) {
355+
omote_log_w("setActiveTab: tabview is NULL, cannot set active tab\n");
356+
return;
357+
}
358+
359+
// Check if the tabview object is still valid
360+
if (!lv_obj_is_valid(tabview)) {
361+
omote_log_w("setActiveTab: tabview object is invalid, cannot set active tab\n");
362+
return;
363+
}
364+
353365
// unsigned long startTime = millis();
354366
if (anim_en == LV_ANIM_ON) {
355367
lv_tabview_set_act(tabview, index, LV_ANIM_ON);

src/applicationInternal/gui/guiMemoryOptimizer.cpp

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,35 @@ void notify_active_tabs_before_delete(t_gui_state *gui_state) {
108108
}
109109
}
110110

111+
void safe_delete_lv_obj(lv_obj_t* obj, const char* obj_name) {
112+
if (obj == NULL) {
113+
return;
114+
}
115+
116+
if (!lv_obj_is_valid(obj)) {
117+
omote_log_w("safe_delete_lv_obj: %s object is invalid, skipping deletion\n", obj_name);
118+
return;
119+
}
120+
121+
lv_obj_del(obj);
122+
}
123+
111124
void clear_tabview(lv_obj_t* tabview, t_gui_state *gui_state) {
112-
if (tabview != NULL) {
113-
// first remove events for the tabview
114-
lv_obj_remove_event_cb(tabview, tabview_tab_changed_event_cb);
115-
lv_obj_remove_event_cb(tabview, tabview_content_is_scrolling_event_cb);
116-
// delete tabview
117-
lv_obj_del(tabview);
118-
tabview = NULL;
125+
if (tabview == NULL) {
126+
return;
119127
}
128+
129+
if (!lv_obj_is_valid(tabview)) {
130+
omote_log_w("clear_tabview: tabview object is invalid, skipping deletion\n");
131+
return;
132+
}
133+
134+
// first remove events for the tabview
135+
lv_obj_remove_event_cb(tabview, tabview_tab_changed_event_cb);
136+
lv_obj_remove_event_cb(tabview, tabview_content_is_scrolling_event_cb);
137+
// delete tabview
138+
lv_obj_del(tabview);
139+
tabview = NULL;
120140

121141
// the gui_list_index_previous is needed for setGUIlistIndicesToBeShown_afterSlide();
122142
gui_state->gui_on_tab[0] = {NULL, "", -1, gui_state->gui_on_tab[0].gui_list_index};
@@ -126,19 +146,9 @@ void clear_tabview(lv_obj_t* tabview, t_gui_state *gui_state) {
126146
}
127147

128148
void clear_panel(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2) {
129-
if (panel != NULL) {
130-
lv_obj_del(panel);
131-
panel = NULL;
132-
}
133-
if (img1 != NULL) {
134-
lv_obj_del(img1);
135-
img1 = NULL;
136-
}
137-
if (img2 != NULL) {
138-
lv_obj_del(img2);
139-
img2 = NULL;
140-
}
141-
149+
safe_delete_lv_obj(panel, "panel");
150+
safe_delete_lv_obj(img1, "img1");
151+
safe_delete_lv_obj(img2, "img2");
142152
}
143153

144154
lv_obj_t* create_tabview() {
@@ -318,6 +328,17 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
318328
#endif
319329
return;
320330
}
331+
332+
// Check available memory before creating many objects
333+
size_t free_heap = esp_get_free_heap_size();
334+
if (free_heap < 5000) { // Less than 5KB free
335+
omote_log_w("fillPanelWithPageIndicator: Low memory (%zu bytes), skipping page indicators\n", free_heap);
336+
lv_obj_add_style(panel, &panel_style, 0);
337+
#ifdef drawRedBorderAroundMainWidgets
338+
lv_obj_add_style(panel, &style_red_border, LV_PART_MAIN);
339+
#endif
340+
return;
341+
}
321342

322343
// This small hidden button enables the page indicator to scroll further
323344
lv_obj_t* btn = lv_btn_create(panel);
@@ -409,8 +430,14 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
409430
// create a breadcrump dot for each gui in the main_gui_list
410431
for (int j=0; j<breadcrumpMainGuiListLength; j++) {
411432
lv_obj_t* dot = lv_obj_create(btn);
433+
if (dot == NULL) {
434+
omote_log_e("fillPanelWithPageIndicator: Failed to create dot object, out of memory\n");
435+
continue;
436+
}
437+
412438
lv_obj_set_size(dot, breadcrumpDotSize, breadcrumpDotSize);
413439
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, LV_PART_MAIN);
440+
414441
// hightlight dot if it is the one for the currently active tab
415442
#if (USE_SCENE_SPECIFIC_GUI_LIST != 0)
416443
if ( ((gui_memoryOptimizer_getActiveGUIlist() == MAIN_GUI_LIST) || !get_scene_has_gui_list(gui_memoryOptimizer_getActiveSceneName()))
@@ -437,6 +464,11 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
437464
if (show_scene_gui_list) {
438465
for (int j=0; j<breadcrumpSceneGuiListLength; j++) {
439466
lv_obj_t* dot = lv_obj_create(btn);
467+
if (dot == NULL) {
468+
omote_log_e("fillPanelWithPageIndicator: Failed to create scene dot object, out of memory\n");
469+
continue;
470+
}
471+
440472
lv_obj_set_size(dot, breadcrumpDotSize, breadcrumpDotSize);
441473
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, LV_PART_MAIN);
442474
if ((gui_memoryOptimizer_getActiveGUIlist() == SCENE_GUI_LIST) && (j == (breadcrumpPosition-1))) {

0 commit comments

Comments
 (0)