LVGL Development Guide*
Chapter 1 Overview*
LVGL (Light and Versatile Graphics Library) is an open-source embedded graphics library designed for resource-constrained devices such as microcontrollers and small embedded systems. It provides a rich set of graphical user interface components and features, supports multiple display and input devices, and is suitable for embedded applications requiring a graphical user interface.
Currently, APUS supports LVGL version 8.3.3 and provides GA acceleration for its underlying drawing operations.
For a more detailed introduction to LVGL, please refer to the official documentation.
Chapter 2 LVGL WMS*
LVGL WMS (Window Manager System) is a multi-window management system that helps users manage multiple window interfaces and implement sliding effects between windows.
Main Principle*
The main principle is that when sliding is required, the frame buffers of the current window and the target window are first saved. Then, based on the sliding offset at each time point during the slide, the two saved frame buffers are combined and output to the final display frame buffer.
Usage Instructions*
For example, create 3 windows with positions at the top-left, right, and bottom, as shown in the figure below. The top-left window can slide right or down, the right window can slide left, and the bottom window can slide up.

Additionally, WMS supports multiple scenes. For example, the scene containing the 3 windows above is called the main scene. A button can be created in the bottom window of the main scene; clicking it enters another scene: Scene 1. Scene 1 has two windows: a left window and a right window. The left window can move right, the right window can slide left, and a button can be created in the right window to return to the bottom window of the main scene when clicked.

Example Code*
To use WMS, you need to implement the create and destroy functions for each window, as well as the window management code.
Window Example Code*
-
Main scene top-left window: wms_scene0_lefttop.c
#include <lvgl/lvgl.h> static lv_obj_t *lv_win_at_scene0_lefttop_create(uint32_t win_id); lv_wms_window_t g_win_at_scene0_lefttop = { .create_func = lv_win_at_scene0_lefttop_create, .window = NULL, }; static void lv_win_at_scene0_lefttop_self_destroy(void) { if (g_win_at_scene0_lefttop.window == NULL) { return; } // Deinitialize WMS lv_wms_deinit(g_win_at_scene0_lefttop.window); lv_obj_del(g_win_at_scene0_lefttop.window); g_win_at_scene0_lefttop.window = NULL; } static lv_obj_t *lv_win_at_scene0_lefttop_create(uint32_t win_id) { if (g_win_at_scene0_lefttop.window) { // Even if the current screen is not destroyed, update the neighbor screen information to ensure correct sliding relationships across different scenes lv_wms_update_neighbor_setting(g_win_at_scene0_lefttop.window, win_id); lv_scr_load(g_win_at_scene0_lefttop.window); return g_win_at_scene0_lefttop.window; } // Create a screen object g_win_at_scene0_lefttop.window = lv_obj_create(NULL); // Add layout code here lv_obj_set_style_bg_color(g_win_at_scene0_lefttop.window, lv_palette_main(LV_PALETTE_BLUE), LV_PART_MAIN); // Initialize WMS lv_wms_init(g_win_at_scene0_lefttop.window); // Set the destroy function; WMS does not automatically destroy objects lv_wms_self_destroy_func_set(g_win_at_scene0_lefttop.window, lv_win_at_scene0_lefttop_self_destroy); // Update neighbor screen information lv_wms_update_neighbor_setting(g_win_at_scene0_lefttop.window, win_id); // Load the current screen lv_scr_load(g_win_at_scene0_lefttop.window); return g_win_at_scene0_lefttop.window; } -
Main scene right window: wms_scene0_right.c
#include <lvgl/lvgl.h> static lv_obj_t *lv_win_at_scene0_right_create(uint32_t win_id); lv_wms_window_t g_win_at_scene0_right = { .create_func = lv_win_at_scene0_right_create, .window = NULL, }; static void lv_win_at_scene0_right_self_destroy(void) { if (g_win_at_scene0_right.window == NULL) { return; } // Deinitialize WMS lv_wms_deinit(g_win_at_scene0_right.window); lv_obj_del(g_win_at_scene0_right.window); g_win_at_scene0_right.window = NULL; } static lv_obj_t *lv_win_at_scene0_right_create(uint32_t win_id) { if (g_win_at_scene0_right.window) { // Even if the current screen is not destroyed, update the neighbor screen information to ensure correct sliding relationships across different scenes lv_wms_update_neighbor_setting(g_win_at_scene0_right.window, win_id); lv_scr_load(g_win_at_scene0_right.window); return g_win_at_scene0_right.window; } // Create a screen object g_win_at_scene0_right.window = lv_obj_create(NULL); // Add layout code here lv_obj_set_style_bg_color(g_win_at_scene0_right.window, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN); // Initialize WMS lv_wms_init(g_win_at_scene0_right.window); // Set the destroy function; WMS does not automatically destroy objects lv_wms_self_destroy_func_set(g_win_at_scene0_right.window, lv_win_at_scene0_right_self_destroy); // Update neighbor screen information lv_wms_update_neighbor_setting(g_win_at_scene0_right.window, win_id); // Load the current screen lv_scr_load(g_win_at_scene0_right.window); return g_win_at_scene0_right.window; } -
Main scene bottom window: wms_scene0_bottom.c
#include <lvgl/lvgl.h> #include "wms_win_manager.h" static lv_obj_t *lv_win_at_scene0_bottom_create(uint32_t win_id); lv_wms_window_t g_win_at_scene0_bottom = { .create_func = lv_win_at_scene0_bottom_create, .window = NULL, }; static void lv_win_at_scene0_bottom_self_destroy(void) { if (g_win_at_scene0_bottom.window == NULL) { return; } // Deinitialize WMS lv_wms_deinit(g_win_at_scene0_bottom.window); lv_obj_del(g_win_at_scene0_bottom.window); g_win_at_scene0_bottom.window = NULL; } static void event_cb(lv_event_t *e) { lv_wms_scene_enter_scene1(); // Button to enter Scene 1 } static lv_obj_t *lv_win_at_scene0_bottom_create(uint32_t win_id) { if (g_win_at_scene0_bottom.window) { // Even if the current screen is not destroyed, update the neighbor screen information to ensure correct sliding relationships across different scenes lv_wms_update_neighbor_setting(g_win_at_scene0_bottom.window, win_id); lv_scr_load(g_win_at_scene0_bottom.window); return g_win_at_scene0_bottom.window; } // Create a screen object g_win_at_scene0_bottom.window = lv_obj_create(NULL); // Add layout code here // Add button lv_obj_t *btn = lv_btn_create(g_win_at_scene0_bottom.window); lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0); lv_obj_set_width(btn, 50); lv_obj_set_height(btn, 50); static lv_style_t style_btn; lv_style_init(&style_btn); lv_style_set_radius(&style_btn, 5); lv_style_set_border_color(&style_btn, lv_color_white()); lv_style_set_border_opa(&style_btn, LV_OPA_50); lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT); lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL); // Add button event lv_obj_set_style_bg_color(g_win_at_scene0_bottom.window, lv_palette_main(LV_PALETTE_ORANGE), LV_PART_MAIN); // Initialize WMS lv_wms_init(g_win_at_scene0_bottom.window); // Set the destroy function; WMS does not automatically destroy objects lv_wms_self_destroy_func_set(g_win_at_scene0_bottom.window, lv_win_at_scene0_bottom_self_destroy); // Update neighbor screen information lv_wms_update_neighbor_setting(g_win_at_scene0_bottom.window, win_id); // Load the current screen lv_scr_load(g_win_at_scene0_bottom.window); return g_win_at_scene0_bottom.window; } -
Scene 1 left window: wms_scene1_left.c
#include <lvgl/lvgl.h> static lv_obj_t *lv_win_at_scene1_left_create(uint32_t win_id); lv_wms_window_t g_win_at_scene1_left = { .create_func = lv_win_at_scene1_left_create, .window = NULL, }; static void lv_win_at_scene1_left_self_destroy(void) { if (g_win_at_scene1_left.window == NULL) { return; } // Deinitialize WMS lv_wms_deinit(g_win_at_scene1_left.window); lv_obj_del(g_win_at_scene1_left.window); g_win_at_scene1_left.window = NULL; } static lv_obj_t *lv_win_at_scene1_left_create(uint32_t win_id) { if (g_win_at_scene1_left.window) { // Even if the current screen is not destroyed, update the neighbor screen information to ensure correct sliding relationships across different scenes lv_wms_update_neighbor_setting(g_win_at_scene1_left.window, win_id); lv_scr_load(g_win_at_scene1_left.window); return g_win_at_scene1_left.window; } // Create a screen object g_win_at_scene1_left.window = lv_obj_create(NULL); // Add layout code here lv_obj_set_style_bg_color(g_win_at_scene1_left.window, lv_palette_main(LV_PALETTE_PURPLE), LV_PART_MAIN); // Initialize WMS lv_wms_init(g_win_at_scene1_left.window); // Set the destroy function; WMS does not automatically destroy objects lv_wms_self_destroy_func_set(g_win_at_scene1_left.window, lv_win_at_scene1_left_self_destroy); // Update neighbor screen information lv_wms_update_neighbor_setting(g_win_at_scene1_left.window, win_id); // Load the current screen lv_scr_load(g_win_at_scene1_left.window); return g_win_at_scene1_left.window; } -
Scene 1 right window: wms_scene1_right.c
#include <lvgl/lvgl.h> #include "wms_win_manager.h" static lv_obj_t *lv_win_at_scene1_right_create(uint32_t win_id); lv_wms_window_t g_win_at_scene1_right = { .create_func = lv_win_at_scene1_right_create, .window = NULL, }; static void lv_win_at_scene1_right_self_destroy(void) { if (g_win_at_scene1_right.window == NULL) { return; } // Deinitialize WMS lv_wms_deinit(g_win_at_scene1_right.window); lv_obj_del(g_win_at_scene1_right.window); g_win_at_scene1_right.window = NULL; } static void event_cb(lv_event_t *e) { lv_wms_scene_exit_scene1(); // Exit Scene 1 } static lv_obj_t *lv_win_at_scene1_right_create(uint32_t win_id) { if (g_win_at_scene1_right.window) { // Even if the current screen is not destroyed, update the neighbor screen information to ensure correct sliding relationships across different scenes lv_wms_update_neighbor_setting(g_win_at_scene1_right.window, win_id); lv_scr_load(g_win_at_scene1_right.window); return g_win_at_scene1_right.window; } // Create a screen object g_win_at_scene1_right.window = lv_obj_create(NULL); // Add layout code here // Add button lv_obj_t *btn = lv_btn_create(g_win_at_scene1_right.window); lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0); lv_obj_set_width(btn, 50); lv_obj_set_height(btn, 50); static lv_style_t style_btn; lv_style_init(&style_btn); lv_style_set_radius(&style_btn, 5); lv_style_set_border_color(&style_btn, lv_color_black()); lv_style_set_border_opa(&style_btn, LV_OPA_50); lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT); lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL); // Add button event lv_obj_set_style_bg_color(g_win_at_scene1_right.window, lv_palette_main(LV_PALETTE_LIGHT_BLUE), LV_PART_MAIN); // Initialize WMS lv_wms_init(g_win_at_scene1_right.window); // Set the destroy function; WMS does not automatically destroy objects lv_wms_self_destroy_func_set(g_win_at_scene1_right.window, lv_win_at_scene1_right_self_destroy); // Update neighbor screen information lv_wms_update_neighbor_setting(g_win_at_scene1_right.window, win_id); // Load the current screen lv_scr_load(g_win_at_scene1_right.window); return g_win_at_scene1_right.window; }
Window Management Example Code*
-
Window management code: wms_win_manager.c
#include <lvgl/lvgl.h> extern lv_wms_window_t g_win_at_scene0_lefttop; extern lv_wms_window_t g_win_at_scene0_right; extern lv_wms_window_t g_win_at_scene0_bottom; extern lv_wms_window_t g_win_at_scene1_left; extern lv_wms_window_t g_win_at_scene1_right; // Declare a window ID enum to refer to WMS Windows; avoid _INVALID_WIN_ID, which is 0 enum { WMS_WIN_ID_INVALID = _INVALID_WIN_ID, WMS_WIN_ID_AT_SCENE0_LEFTTOP, WMS_WIN_ID_AT_SCENE0_RIGHT, WMS_WIN_ID_AT_SCENE0_BOTTOM, WMS_WIN_ID_AT_SCENE1_LEFT, WMS_WIN_ID_AT_SCENE1_RIGHT, }; // Declare a scene ID enum to refer to WMS Scenes; avoid _INVALID_SCENE_ID, which is 0 enum { WMS_SCENE_ID_INVALID = _INVALID_SCENE_ID, WMS_SCENE0_ID, WMS_SCENE1_ID, }; // Define a table to associate Window IDs with WMS Windows static const lv_wms_window_map_t WMS_WINDOW_MAP[] = { {WMS_WIN_ID_AT_SCENE0_LEFTTOP, &g_win_at_scene0_lefttop}, {WMS_WIN_ID_AT_SCENE0_RIGHT, &g_win_at_scene0_right}, {WMS_WIN_ID_AT_SCENE0_BOTTOM, &g_win_at_scene0_bottom}, {WMS_WIN_ID_AT_SCENE1_LEFT, &g_win_at_scene1_left}, {WMS_WIN_ID_AT_SCENE1_RIGHT, &g_win_at_scene1_right}, }; #define WMS_WINDOW_COUNT (sizeof(WMS_WINDOW_MAP)/sizeof(WMS_WINDOW_MAP[0])) // Define a 2D array to describe the sliding relationships (up/down/left/right) of the main scene #define SCENE0_X_ELEMS 2 #define SCENE0_Y_ELEMS 2 static uint32_t s_scene0[SCENE0_Y_ELEMS][SCENE0_X_ELEMS] = { {WMS_WIN_ID_AT_SCENE0_LEFTTOP, WMS_WIN_ID_AT_SCENE0_RIGHT}, {WMS_WIN_ID_AT_SCENE0_BOTTOM,} }; #define SCENE1_X_ELEMS 2 #define SCENE1_Y_ELEMS 1 static uint32_t s_scene1[SCENE1_Y_ELEMS][SCENE1_X_ELEMS] = { {WMS_WIN_ID_AT_SCENE1_LEFT, WMS_WIN_ID_AT_SCENE1_RIGHT}, }; // Define an array containing all WMS Scenes, including the Scene ID, the main window of the scene, the number of horizontal and vertical windows in the scene, and the sliding relationships between windows within the scene static const lv_wms_scene_t WMS_SCENE_MAP[] = { {WMS_SCENE0_ID, WMS_WIN_ID_AT_SCENE0_LEFTTOP, SCENE0_X_ELEMS, SCENE0_Y_ELEMS, (uint32_t *)s_scene0}, {WMS_SCENE1_ID, WMS_WIN_ID_AT_SCENE1_LEFT, SCENE1_X_ELEMS, SCENE1_Y_ELEMS, (uint32_t *)s_scene1}, }; #define WMS_SCENE_COUNT (sizeof(WMS_SCENE_MAP)/sizeof(WMS_SCENE_MAP[0])) void lv_wms_scene_init(void) { // Initialize the Scene manager and load the default window of the default scene lv_wms_scene_config_t config = { .p_win_map_table = (lv_wms_window_map_t *)WMS_WINDOW_MAP, .p_scene_map_table = (lv_wms_scene_t *)WMS_SCENE_MAP, .win_amount = WMS_WINDOW_COUNT, .scene_amount = WMS_SCENE_COUNT, .startup_scene_id = WMS_SCENE0_ID, .startup_win_id = WMS_WIN_ID_AT_SCENE0_LEFTTOP, // Window displayed at startup, i.e., the main window }; lv_wms_scene_startup(&config); } // Enter Scene 1 void lv_wms_scene_enter_scene1(void) { lv_wms_scene_enter_into(WMS_SCENE1_ID); } // Exit Scene 1 void lv_wms_scene_exit_scene1(void) { lv_wms_scene_exit_cur_win(); } -
Window management header file: wms_win_manager.h
#ifndef __WMS_WIN_MANAGER_H__ #define __WMS_WIN_MANAGER_H__ void lv_wms_scene_init(void); void lv_wms_scene_enter_scene1(void); void lv_wms_scene_exit_scene1(void); #endif
WMS Startup*
After LVGL initialization is complete, call lv_wms_scene_init() to initialize and enter the main window of WMS.