文章目录
1、 前置条件
- SDL 环境搭建, 参考:https://blog.csdn.net/qq_51355375/article/details/147051709?spm=1011.2415.3001.5331
- lvgl 源码下载: https://github.com/lvgl/lvgl/tree/release/v9.2
2. LVGL 加入
在SDL测试工程中新建third_party 目录, 将lvgl 复制到这个目录下,删除其他不需要的目录和文件,保留src, examples和根目录下的头文件, 更改包含版本的lvgl-v9.2目录为lvgl
3. 修改CmakLists.txt
cmake_minimum_required(VERSION 3.30)
project(sdl3_lvgl)
set(CMAKE_C_STANDARD 11)
# 包含目录
include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/third_party/lvgl
${CMAKE_SOURCE_DIR}/third_party/lvgl/../
${CMAKE_SOURCE_DIR}/third_party/lvgl/src
# ${CMAKE_SOURCE_DIR}/third_party/lvgl/examples/porting
)
# 递归收集 .c 文件(从指定根目录开始)
# LVGL
file(GLOB_RECURSE LVGL_SRC
LIST_DIRECTORIES false # 忽略目录,仅保留文件
"${CMAKE_SOURCE_DIR}/third_party/lvgl/src/*.c"
)
# 可选:排除不需要的目录
# list(FILTER SOURCES EXCLUDE REGEX ".*/tests?/.*") # 排除 test 或 tests 目录
# 源文件
file(GLOB_RECURSE SOURCES
LIST_DIRECTORIES false
"${CMAKE_SOURCE_DIR}/src/*.c"
)
# 链接库路径
link_directories(${CMAKE_SOURCE_DIR}/lib)
# message(STATUS "Project source dir: ${PROJECT_SOURCE_DIR}")
# message(STATUS "Main.c path: ${PROJECT_SOURCE_DIR}/src/main.c")
# 可执行文件
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/main.c ${SOURCES} ${LVGL_SRC})
# 链接库
target_link_libraries(${PROJECT_NAME}
SDL3
)
# 拷贝库文件 文件
file(COPY ${PROJECT_SOURCE_DIR}/lib/SDL3.dll DESTINATION ${PROJECT_BINARY_DIR})
4. lvgl 适配
4.1 修改lvgl配置文件
将lv_conf_template.h 预编译指令改为1, 并改文件名为lv_conf.h,不然会找不到这个头文件
4. lvgl 整合
4.1 引入 lvgl
4.1 初始化SDL
#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_ARGB8888))
#define WIDTH (600) // 屏幕宽度
#define HEIGHT (400) // 屏幕高度
#define BUF_HEIGHT 40 // 小行缓冲
static SDL_Window *window;
static SDL_Renderer *renderer;
static SDL_Texture *texture;
static volatile bool running = true;
int initSDL() {
// 初始化SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_Log(" SDL init falid : %s", SDL_GetError());
return -1;
}
// 创建窗口
window = SDL_CreateWindow("SDL3", WIDTH, HEIGHT, SDL_WINDOW_HIGH_PIXEL_DENSITY);
// // 创建渲染器
renderer = SDL_CreateRenderer(window, NULL);
// SDL_CreateWindowAndRenderer("SDL3", HOR, VER, SDL_WINDOW_HIGH_PIXEL_DENSITY, &window, &renderer);
// 创建纹理
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, // 改为流式访问
WIDTH, HEIGHT);
if (!window || !renderer || !texture) {
SDL_Log("initSDL fail: %s", SDL_GetError());
SDL_Quit();
running = false;
return -1;
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // 设置背景为白色
return 0;
}
4.1 初始化并注册显示设备
void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
SDL_Rect src_rect = {
.x = area->x1,
.y = area->y1,
.w = area->x2 - area->x1 + 1,
.h = area->y2 - area->y1 + 1
};
// 更新纹理的特定区域
SDL_UpdateTexture(texture, &src_rect, px_map, WIDTH * BYTE_PER_PIXEL);
// 渲染整个纹理到窗口
SDL_RenderTexture(renderer, texture, NULL, NULL);
lv_display_flush_ready(disp);
}
void lv_port_disp_init(void) {
lv_display_t *disp = lv_display_create(WIDTH, HEIGHT);
lv_display_set_flush_cb(disp, disp_flush);
lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888);
static uint8_t buf1[WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL];
static uint8_t buf2[WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL];
lv_display_set_buffers(disp, buf1, buf2,
WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL,
LV_DISPLAY_RENDER_MODE_PARTIAL);
}
这里的lv_port_disp_init 函数其实就是 lv_port_disp_template.c 示例中的重定义:
5. 创建lvgl布局控件
// 创建带布局的UI界面
void create_ui(void) {
// 获取当前活跃的屏幕
lv_obj_t *scr = lv_scr_act();
// 创建主容器(使用Flex布局)
lv_obj_t *cont = lv_obj_create(scr);
lv_obj_set_size(cont, WIDTH - 40, HEIGHT - 40); // 留出边距
lv_obj_center(cont);
// 设置Flex布局参数
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); // 垂直排列
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
// 添加标题标签
lv_obj_t *title = lv_label_create(cont);
lv_label_set_text(title, "LVGL SDL3 Demo");
// lv_obj_set_style_text_font(title, &lv_font_montserrat_28, 0);
lv_obj_set_style_text_color(title, lv_color_hex(0x003a57), 0);
// 创建按钮容器(水平排列)
lv_obj_t *btn_cont = lv_obj_create(cont);
lv_obj_set_size(btn_cont, lv_pct(100), 80);
lv_obj_set_flex_flow(btn_cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(btn_cont, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_remove_style_all(btn_cont); // 移除默认样式
// 添加三个不同样式的按钮
const char *btns[] = {"sure", "cancl", "more..."};
for(int i = 0; i < 3; i++) {
lv_obj_t *btn = lv_button_create(btn_cont);
lv_obj_set_size(btn, 100, 40);
// 按钮标签
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, btns[i]);
lv_obj_center(label);
// 设置不同颜色样式
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(i == 0 ? 0x00aa00 :
(i == 1 ? 0xaa0000 : 0x555555)));
lv_style_set_radius(&style_btn, 8);
lv_obj_add_style(btn, &style_btn, 0);
}
// 添加信息文本区域
lv_obj_t *info = lv_textarea_create(cont);
lv_textarea_set_text(info, "init.........................");
lv_obj_set_size(info, lv_pct(100), 120);
lv_obj_set_style_border_width(info, 2, 0);
lv_obj_set_style_border_color(info, lv_color_hex(0xcccccc), 0);
}
6. 主函数
主函数中包含lvgl和sdl 的相关初始化和设置
int main(int argc, char *argv[]) {
initSDL();
lv_init();
lv_port_disp_init();
create_ui();
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) running = false;
}
lv_timer_handler();
SDL_RenderClear(renderer);
SDL_RenderTexture(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
static Uint32 last = 0;
Uint32 now = SDL_GetTicks();
lv_tick_inc(now - last);
last = now;
SDL_Delay(1);
}
// 释放资源
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
7. 运行测试及完整代码
测试:
完整代码:
#include "SDL3/SDL.h"
#include "lvgl/lvgl.h"
#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_ARGB8888))
#define WIDTH (600) // 屏幕宽度
#define HEIGHT (400) // 屏幕高度
#define BUF_HEIGHT 40 // 小行缓冲
static SDL_Window *window;
static SDL_Renderer *renderer;
static SDL_Texture *texture;
static volatile bool running = true;
int initSDL() {
// 初始化SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_Log(" SDL init falid : %s", SDL_GetError());
return -1;
}
// 创建窗口
window = SDL_CreateWindow("SDL3", WIDTH, HEIGHT, SDL_WINDOW_HIGH_PIXEL_DENSITY);
// // 创建渲染器
renderer = SDL_CreateRenderer(window, NULL);
// SDL_CreateWindowAndRenderer("SDL3", HOR, VER, SDL_WINDOW_HIGH_PIXEL_DENSITY, &window, &renderer);
// 创建纹理
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, // 改为流式访问
WIDTH, HEIGHT);
if (!window || !renderer || !texture) {
SDL_Log("initSDL fail: %s", SDL_GetError());
SDL_Quit();
running = false;
return -1;
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // 设置背景为白色
return 0;
}
void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
SDL_Rect src_rect = {
.x = area->x1,
.y = area->y1,
.w = area->x2 - area->x1 + 1,
.h = area->y2 - area->y1 + 1
};
// 更新纹理的特定区域
SDL_UpdateTexture(texture, &src_rect, px_map, WIDTH * BYTE_PER_PIXEL);
// 渲染整个纹理到窗口
SDL_RenderTexture(renderer, texture, NULL, NULL);
lv_display_flush_ready(disp);
}
void lv_port_disp_init(void) {
lv_display_t *disp = lv_display_create(WIDTH, HEIGHT);
lv_display_set_flush_cb(disp, disp_flush);
lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888);
static uint8_t buf1[WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL];
static uint8_t buf2[WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL];
lv_display_set_buffers(disp, buf1, buf2,
WIDTH * BUF_HEIGHT * BYTE_PER_PIXEL,
LV_DISPLAY_RENDER_MODE_PARTIAL);
}
// 创建带布局的UI界面
void create_ui(void) {
// 获取当前活跃的屏幕
lv_obj_t *scr = lv_scr_act();
// 创建主容器(使用Flex布局)
lv_obj_t *cont = lv_obj_create(scr);
lv_obj_set_size(cont, WIDTH - 40, HEIGHT - 40); // 留出边距
lv_obj_center(cont);
// 设置Flex布局参数
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); // 垂直排列
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
// 添加标题标签
lv_obj_t *title = lv_label_create(cont);
lv_label_set_text(title, "LVGL SDL3 Demo");
// lv_obj_set_style_text_font(title, &lv_font_montserrat_28, 0);
lv_obj_set_style_text_color(title, lv_color_hex(0x003a57), 0);
// 创建按钮容器(水平排列)
lv_obj_t *btn_cont = lv_obj_create(cont);
lv_obj_set_size(btn_cont, lv_pct(100), 80);
lv_obj_set_flex_flow(btn_cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(btn_cont, LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_remove_style_all(btn_cont); // 移除默认样式
// 添加三个不同样式的按钮
const char *btns[] = {"sure", "cancl", "more..."};
for(int i = 0; i < 3; i++) {
lv_obj_t *btn = lv_button_create(btn_cont);
lv_obj_set_size(btn, 100, 40);
// 按钮标签
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, btns[i]);
lv_obj_center(label);
// 设置不同颜色样式
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(i == 0 ? 0x00aa00 :
(i == 1 ? 0xaa0000 : 0x555555)));
lv_style_set_radius(&style_btn, 8);
lv_obj_add_style(btn, &style_btn, 0);
}
// 添加信息文本区域
lv_obj_t *info = lv_textarea_create(cont);
lv_textarea_set_text(info, "init.........................");
lv_obj_set_size(info, lv_pct(100), 120);
lv_obj_set_style_border_width(info, 2, 0);
lv_obj_set_style_border_color(info, lv_color_hex(0xcccccc), 0);
}
int main(int argc, char *argv[]) {
initSDL();
lv_init();
lv_port_disp_init();
create_ui();
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) running = false;
}
lv_timer_handler();
SDL_RenderClear(renderer);
SDL_RenderTexture(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
static Uint32 last = 0;
Uint32 now = SDL_GetTicks();
lv_tick_inc(now - last);
last = now;
SDL_Delay(1);
}
// 释放资源
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
后续将尝试整合lvgl 和 sdl 事件, 感兴趣的可加qq群讨论: 1046502146