从零写 OS 内核-第三十二篇:集成 LVGL —— 用开源 GUI 框架实现文本编辑器

"从零造轮子虽有趣,但工业级 GUI 需要成熟框架。
今天,我们将 LVGL(Light and Versatile Graphics Library)移植到我们的 OS,实现专业级文本编辑器!"

在前几篇中,我们从零实现了:
Framebuffer 驱动
窗口管理器
位图文字渲染
画板工具

但手写 GUI 控件(按钮、输入框、滚动条)极其耗时。
真正的应用开发需要 成熟的 GUI 框架——
LVGL 正是为嵌入式系统设计的轻量级开源 GUI 库!

今天,我们就来:
移植 LVGL 到我们的 OS
实现 Display Driver 与 Input Driver
开发 LVGL 文本编辑器
集成到窗口管理器中

让你的应用拥有专业级 UI 体验


🧩 一、为什么选择 LVGL?

LVGL 核心优势:

| 特性 | 说明 | |——|——| | 轻量级 | 仅需 64KB Flash + 10KB RAM | | 开源免费 | MIT 许可证,商用无忧 | | 功能丰富 | 按钮、滑块、文本框、列表、图表… | | 硬件抽象 | 通过 Display/Input Driver 适配任意平台 | | 活跃社区 | GitHub 15k+ stars,持续更新 |

💡 LVGL 被用于智能家居、医疗设备、汽车仪表盘等嵌入式产品


🔌 二、LVGL 移植:Display Driver

LVGL 通过 Display Driver 将绘图命令转换为像素操作。

1. 实现 disp_flush

// user/lvgl/disp_driver.c
static void disp_flush(lv_disp_drv_t *disp, 
                       const lv_area_t *area, 
                       lv_color_t *color_p) {
    // 1. 获取 Framebuffer
    extern uint32_t *fb_buffer;
    extern int fb_width;
    
    // 2. 将 LVGL 颜色转换为 RGBA
    for (int y = area->y1; y <= area->y2; y++) {
        for (int x = area->x1; x <= area->x2; x++) {
            lv_color_t color = *color_p;
            uint32_t rgba = (0xFF << 24) | 
                           (color.blue << 16) | 
                           (color.green << 8) | 
                           (color.red);
            fb_buffer[y * fb_width + x] = rgba;
            color_p++;
        }
    }
    
    // 3. 通知 LVGL 刷新完成
    lv_disp_flush_ready(disp);
}

2. 注册 Display Driver

void lvgl_init() {
    // 1. 初始化 LVGL
    lv_init();
    
    // 2. 配置 Display Driver
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_flush;
    disp_drv.hor_res = 1024;  // 屏幕宽度
    disp_drv.ver_res = 768;   // 屏幕高度
    
    // 3. 注册
    lv_disp_drv_register(&disp_drv);
}

🔑 disp_flush 是 LVGL 与 Framebuffer 的桥梁


🖱️ 三、LVGL 移植:Input Driver

LVGL 通过 Input Driver 处理鼠标/键盘事件。

1. 实现 indev_read

// user/lvgl/indev_driver.c
static bool indev_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
    static int last_x = 0, last_y = 0;
    static bool last_pressed = false;
    
    // 1. 从 Display Server 获取鼠标状态
    extern struct global_mouse_state mouse_state;
    
    data->point.x = mouse_state.x;
    data->point.y = mouse_state.y;
    data->state = (mouse_state.buttons & 1) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
    
    // 2. 检测点击事件
    bool clicked = false;
    if (data->state == LV_INDEV_STATE_RELEASED && last_pressed) {
        clicked = true;
    }
    
    last_x = data->point.x;
    last_y = data->point.y;
    last_pressed = (data->state == LV_INDEV_STATE_PRESSED);
    
    return clicked; // 是否有新事件
}

2. 注册 Input Driver

void lvgl_input_init() {
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER; // 鼠标
    indev_drv.read_cb = indev_read;
    
    lv_indev_drv_register(&indev_drv);
}

💡 LVGL 会自动处理按钮悬停、点击反馈等交互


📝 四、文本编辑器实现

1. 创建主窗口

// user/text_editor.c
void text_editor_create() {
    // 1. 创建主屏幕对象
    lv_obj_t *scr = lv_scr_act();
    
    // 2. 创建标题标签
    lv_obj_t *title = lv_label_create(scr);
    lv_label_set_text(title, "MyOS Text Editor");
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
    
    // 3. 创建文本区域(Text Area)
    lv_obj_t *ta = lv_textarea_create(scr);
    lv_textarea_set_one_line(ta, false); // 多行
    lv_obj_set_size(ta, 600, 400);
    lv_obj_align(ta, LV_ALIGN_CENTER, 0, 0);
    lv_textarea_set_max_length(ta, 1024);
    
    // 4. 创建状态栏
    lv_obj_t *status = lv_label_create(scr);
    lv_label_set_text(status, "Lines: 1 | Characters: 0");
    lv_obj_align(status, LV_ALIGN_BOTTOM_MID, 0, -10);
    
    // 5. 保存引用(用于更新)
    editor.ta = ta;
    editor.status = status;
}

2. 处理文本变化

static void ta_event_cb(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *ta = lv_event_get_target(e);
    
    if (code == LV_EVENT_VALUE_CHANGED) {
        // 更新状态栏
        const char *txt = lv_textarea_get_text(ta);
        int lines = 1;
        int chars = 0;
        
        for (const char *p = txt; *p; p++) {
            if (*p == '\n') lines++;
            chars++;
        }
        
        char status[64];
        sprintf(status, "Lines: %d | Characters: %d", lines, chars);
        lv_label_set_text(editor.status, status);
    }
}

// 在 text_editor_create 中注册回调
lv_obj_add_event_cb(ta, ta_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

3. 添加菜单栏(简化版)

void create_menu_bar() {
    lv_obj_t *scr = lv_scr_act();
    
    // 新建按钮
    lv_obj_t *btn_new = lv_btn_create(scr);
    lv_obj_set_size(btn_new, 80, 30);
    lv_obj_align(btn_new, LV_ALIGN_TOP_LEFT, 10, 50);
    
    lv_obj_t *label = lv_label_create(btn_new);
    lv_label_set_text(label, "New");
    
    // 打开按钮
    lv_obj_t *btn_open = lv_btn_create(scr);
    lv_obj_set_size(btn_open, 80, 30);
    lv_obj_align(btn_open, LV_ALIGN_TOP_LEFT, 100, 50);
    
    label = lv_label_create(btn_open);
    lv_label_set_text(label, "Open");
    
    // 绑定事件
    lv_obj_add_event_cb(btn_new, btn_new_event, LV_EVENT_CLICKED, NULL);
    lv_obj_add_event_cb(btn_open, btn_open_event, LV_EVENT_CLICKED, NULL);
}

🚀 五、集成到窗口管理器

1. Text Editor 应用入口

// user/text_editor_main.c
void _start() {
    // 1. 连接到 Display Server
    display_t *disp = display_connect();
    display_create_window(disp, 800, 600);
    
    // 2. 初始化 LVGL
    lvgl_init();
    lvgl_input_init();
    
    // 3. 创建文本编辑器
    text_editor_create();
    create_menu_bar();
    
    // 4. 主循环:处理 LVGL 任务
    while (1) {
        // 处理 Display Server 事件(鼠标/键盘)
        process_display_events();
        
        // LVGL 任务处理
        uint32_t time_ms = get_system_time();
        lv_timer_handler_run_in_period(5); // 5ms 刷新
        
        // 提交帧
        display_commit(disp);
        usleep(10000); // 10ms
    }
}

2. LVGL 与 Display Server 协同

  • Display Server:提供鼠标/键盘事件 + Framebuffer
  • LVGL:消费事件 + 绘制 UI
  • Text Editor:业务逻辑(文件操作、状态更新)

LVGL 完全运行在用户态,无需内核修改


📁 六、部署与构建

1. LVGL 编译配置

// lv_conf.h (关键配置)
#define LV_COLOR_DEPTH     32
#define LV_COLOR_16_SWAP   0
#define LV_FONT_DEFAULT    &lv_font_montserrat_14
#define LV_USE_TEXTAREA    1
#define LV_USE_LABEL       1
#define LV_USE_BTN         1

2. Makefile 集成

# user/Makefile
LVGL_DIR = ../lvgl

text_editor: text_editor.c $(LVGL_DIR)/lvgl.h
	$(CC) $(CFLAGS) -I$(LVGL_DIR) \
	      -I$(LVGL_DIR)/src \
	      -o $@ $^ \
	      $(LVGL_DIR)/src/core/*.c \
	      $(LVGL_DIR)/src/widgets/*.c \
	      $(LVGL_DIR)/src/misc/*.c \
	      $(LVGL_DIR)/src/draw/*.c \
	      $(LVGL_DIR)/src/font/*.c \
	      syscalls.c display_client.c

3. .desktop 文件

[Desktop Entry]
Name=Text Editor
Exec=/bin/text_editor
Icon=/usr/share/icons/text_editor.png
Type=Application
Categories=Utility;TextEditor;

🧪 七、运行效果

启动 Text Editor:

  1. 显示 LVGL 渲染的 UI
    • 顶部标题 "MyOS Text Editor"
    • 中央多行文本区域
    • 底部状态栏 "Lines: 1 Characters: 0"
    • 左侧 "New"/"Open" 按钮
  2. 鼠标交互
    • 按钮悬停变色
    • 点击按钮触发事件
    • 在文本区域点击/拖拽选择文字
  3. 文本编辑
    • 输入文字实时更新状态栏
    • 支持多行、退格、回车
    • 光标闪烁(LVGL 内置)

专业级文本编辑器运行成功


⚙️ 八、扩展方向

  1. 文件 I/O
    • 实现 Open/Save 功能(通过 ext2 系统调用)
    • 支持文件对话框(LVGL 内置)
  2. 主题定制
    • 修改 LVGL 颜色/字体/动画
    • 实现暗色主题
  3. 高级编辑功能
    • 查找/替换
    • 语法高亮(需自定义绘制)
  4. 多窗口支持
    • 每个文件一个 LVGL 屏幕
    • 通过窗口管理器管理

💡 LVGL 的 lv_fs 接口可直接对接你的 ext2 文件系统


💬 写在最后

集成 LVGL 是开发效率的飞跃
它让我们从 GUI 基础设施中解放,
专注于应用逻辑本身。

今天你创建的第一个 LVGL 文本编辑器,
正是无数嵌入式 GUI 应用的起点。

🌟 站在巨人的肩膀上,才能看得更远。


📬 动手挑战
实现 "Save" 功能,将文本保存到 /home/user/document.txt
欢迎在评论区分享你的 LVGL 应用截图!

👇 下一篇你想看:LVGL 文件浏览器,还是 多窗口文档编辑器


#操作系统 #内核开发 #LVGL #GUI #文本编辑器 #嵌入式GUI #从零开始


📢 彩蛋:关注后回复关键词 "lvgl",获取:

  • LVGL 移植模板(Display/Input Driver)
  • 完整文本编辑器源码
  • LVGL + MyOS 构建脚本