-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Description
LVGL version
v9.3.0-556-gc016f72d4
Platform
Platform: Ubuntu Linux 22.04 / GLFW + OpenGL ES backend
Compiler: GCC 11.4
What happened?
After calling lv_opengles_window_delete() on the only open window, a SEGFAULT is caused due to stack-overflow (infinite recursion). Hence, it isn't possible to open a window, close it and open another. The code appears to be trying to cleanup and exit as it was the last window, but the code is buggy and would have called exit(0) anyway, which isn't desirable. I have #define LV_LOG_LEVEL LV_LOG_LEVEL_INFO in lv_config.h which might impact it, since if logging it off the recursion may not occur (but the exit(0) still would).
I had Claude.ai create a small reproduction .cpp and explain what I found using gdb.
Thank you.
-- AI text below (take care):
Calling lv_opengles_window_delete() on the last window causes the program to SEGFAULT, making it impossible to programmatically close windows without terminating the entire application.
Expected Behavior
When closing a window programmatically via lv_opengles_window_delete(), the window should close and the program should continue running normally. The application should be able to:
- Close all windows
- Continue executing code
- Potentially create new windows later
- Clean up and exit gracefully when ready
Actual Behavior
When lv_opengles_window_delete() is called on the last window:
- The function detects the window list is empty (lv_opengles_glfw.c:189)
- It calls
lv_glfw_window_quit()(lv_opengles_glfw.c:190) lv_glfw_window_quit()callsglfwTerminate()(line 441) and setsglfw_inited = false(line 442)- Then it calls
lv_deinit()(line 444) - During
lv_deinit(), the logging system tries to get a timestamp vialv_tick_get() lv_tick_get()callslv_glfw_tick_count_callback()which callsglfwGetTime()- Since GLFW was already terminated in step 3, this triggers a GLFW error
- The GLFW error callback
glfw_error_cb()tries to log the error usingLV_LOG_ERROR() - This triggers another call to
lv_tick_get(), creating infinite recursion - The stack overflows, causing a SEGFAULT
- The program never reaches
exit(0)at line 446
Root Cause
There are two related problems in src/drivers/opengles/lv_opengles_glfw.c:
Problem 1: Automatic shutdown when last window closes
// Line 189-190 in lv_opengles_window_delete()
if(lv_ll_is_empty(&glfw_window_ll)) {
lv_glfw_window_quit(); // Automatically called!
...
}Problem 2: Incorrect shutdown order causing infinite recursion
// Line 436-446
static void lv_glfw_window_quit(void)
{
lv_timer_delete(update_handler_timer);
update_handler_timer = NULL;
glfwTerminate(); // Line 441: Terminates GLFW
glfw_inited = false; // Line 442: Marks GLFW as not initialized
lv_deinit(); // Line 444: Tries to deinit LVGL
// But during deinit, logging calls lv_tick_get()
// which calls glfwGetTime() on terminated GLFW
// triggering error callback that tries to log
// causing infinite recursion and stack overflow!
exit(0); // Line 446: Never reached due to SEGFAULT above
}The proper order should be:
- Delete the LVGL timer first ✓
- Call
lv_deinit()BEFORE terminating GLFW (so logging still works) - Then call
glfwTerminate() - Optionally call
exit(0)or let the application decide
How to reproduce?
/*
* LVGL Window Close Bug Demonstration
*
* This program demonstrates that calling lv_opengles_window_delete() on the last
* window causes the program to SEGFAULT, making it impossible to programmatically
* close windows without terminating the entire application.
*
* Expected behavior: Window should close and program should continue running.
* Actual behavior: Program crashes/hangs in an infinite loop when the last window
* is deleted, because lv_opengles_window_delete() calls
* lv_glfw_window_quit() which SEGFAULTs before attempting to call exit(0).
*
* Root cause: In lv_opengles_glfw.c:
* - Line 189-190: When window list becomes empty, calls lv_glfw_window_quit()
* - Line 436-446: lv_glfw_window_quit() calls glfwTerminate(), lv_deinit(),
* and exit(0)
*
* Build manually:
* g++ -std=c++17 lvgl_window_close_bug.cpp \
* -I../lvgl \
* -Lbuild/lvgl/lib \
* -llvgl -lglfw -lGL -lGLEW -lfreetype -lpng -lpthread \
* -o lvgl_window_close_bug
*/
#include <stdio.h>
#include <unistd.h>
#include <GLFW/glfw3.h>
#include <lvgl.h>
int main() {
printf("=== LVGL Window Close Bug Demonstration ===\n\n");
// Initialize GLFW
printf("1. Initializing GLFW...\n");
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return 1;
}
printf(" GLFW initialized successfully\n\n");
// Set up OpenGL ES context hints
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// Initialize LVGL
printf("2. Initializing LVGL...\n");
lv_init();
printf(" LVGL initialized successfully\n\n");
// Create LVGL window
printf("3. Creating LVGL window (640x480)...\n");
lv_opengles_window_t* lv_window = lv_opengles_glfw_window_create(640, 480, true);
if (!lv_window) {
fprintf(stderr, "Failed to create LVGL window\n");
return 1;
}
printf(" LVGL window created at %p\n\n", (void*)lv_window);
// Create display texture
printf("4. Creating display texture...\n");
lv_display_t* lv_display = lv_opengles_texture_create(640, 480);
if (!lv_display) {
fprintf(stderr, "Failed to create display texture\n");
lv_opengles_window_delete(lv_window);
return 1;
}
printf(" Display texture created at %p\n\n", (void*)lv_display);
// Add texture to window
printf("5. Adding texture to window...\n");
int32_t texture_id = lv_opengles_texture_get_texture_id(lv_display);
lv_opengles_window_texture_t* window_texture = lv_opengles_window_add_texture(
lv_window, texture_id, 640, 480
);
if (!window_texture) {
fprintf(stderr, "Failed to add texture to window\n");
lv_display_delete(lv_display);
lv_opengles_window_delete(lv_window);
return 1;
}
printf(" Window texture created at %p\n", (void*)window_texture);
printf(" Texture ID: %d\n\n", texture_id);
// Wait a bit to show the window
printf("6. Window created and displayed. Waiting 2 seconds...\n");
sleep(2);
printf(" Done waiting\n\n");
// Now close the window programmatically
printf("7. Attempting to close window programmatically...\n");
printf(" Step 7a: Removing window texture...\n");
lv_opengles_window_texture_remove(window_texture);
printf(" Step 7a: ✓ Texture removed successfully\n\n");
printf(" Step 7b: Deleting display...\n");
lv_display_delete(lv_display);
printf(" Step 7b: ✓ Display deleted successfully\n\n");
printf(" Step 7c: Deleting LVGL window...\n");
printf(" WARNING: This will SEGFAULT if it's the last window!\n");
printf(" About to call lv_opengles_window_delete()...\n");
fflush(stdout);
lv_opengles_window_delete(lv_window);
// This line will NEVER be reached because lv_opengles_window_delete() causes infinite recursion
// (I think it calls lvgl_deinit and then tries to use log causing an error it tried to log)
printf(" Step 7c: ✓ Window deleted successfully\n\n");
printf("8. Cleaning up...\n");
printf(" This code should be reachable, but it won't be SEGFAULT\n");
glfwTerminate();
printf("\n=== Test completed successfully ===\n");
printf("If you see this message, the bug has been fixed!\n");
return 0;
}