Skip to content

1. 简介

1.1 本文的目的

AWTK 全称 Toolkit AnyWhere,是 ZLG 开发的开源 GUI 引擎,详情可前往 官网 查看。AWTK 的源码可前往 GitHub 仓库 下载。

本文是为了介绍如何把 AWTK 移植到各种嵌入式平台上而编写的,并且还将说明 AWTK 代码结构、堆栈需求以及相关功能模块等各种信息,帮助读者全方面认知 AWTK。

本文基于 AWTK 1.6.1 编写,由于不同版本 AWTK 的代码结构和资源有所不同,所以其他版本可能会存在差异。

1.2 开发环境(编译器)

需要支持 gnu99 版本或者以上的 C/C++ 编译器。

1.3 AWTK 主要运行流程

启动 AWTK 程序时,运行流程如下图所示,其中标红的函数是在移植 AWTK 时需重点关注的函数,它们通常需要根据实际平台实现或者重载:

流程图中的 platform_disaptch_input 函数是在使用 awtk/src/main_loop/main_loop_raw.inc 文件实现设备事件分发时才需要重载,具体方法详见下文第5章。

1、main 函数

AWTK 程序启动时,默认调用 AWTK 内置的 main 函数,函数实现请参考:awtk/src/awtk_main.inc,它会依次调用下列主要函数:

(1) tk_init 函数:初始化 AWTK。

(2) assets_init 函数:加载项目资源。

(3) application_init 函数:初始化应用程序,通常由用户实现。

(4) tk_run 函数:启动 AWTK GUI 主循环(main_loop)。

(5) application_exit 函数:退出应用程序,用于释放应用程序相关资源,通常由用户实现。

如果用户想要使用系统 main 函数,可以在 awtk_main.inc 文件前定义宏 USE_GUI_MAIN,并且在系统 main 函数中调用 gui_app_start 函数启动 AWTK,该函数与 AWTK 内置 main 函数的实现类似,示例代码如下:

c
#define USE_GUI_MAIN 1   /* 使用系统 main 函数时,请定义本宏*/

#include "awtk_main.inc"
int main(int argc, char* argv[]) {
  return gui_app_start(LCD_WIDTH, LCD_HEIGHT); /* 启动 AWTK(需指定 LCD 的宽高) */
}

2、tk_init 函数

tk_init 函数主要用于初始化 AWTK,它会依次调用下列主要函数:

(1) platform_prepare 函数:初始化平台相关的准备模块,比如内存管理器和时间管理器等。

(2) main_loop_init 函数:初始化 AWTK 的主循环,其中会调用 platform_create_lcd 函数初始化 LCD,通常用户会在该函数中设置 LCD 相关的 Framebuffer。

(3) 完成其他的一些初始化任务。

3、application_init 函数

application_init 函数一般用于应用程序初始化,用户通常在该函数中执行程序初始化代码,并打开第一个 GUI 窗口。

4、tk_run 函数

tk_run 函数用于启动 AWTK 的主循环,它会让 GUI 线程不断循环调用以下函数:

(1) main_loop_dispatch_input 函数:获取输入设备的信息,其中会调用 platform_disaptch_input 函数将输入设备信息分发至主循环,用户通常会在该函数中实现输入设备的移植。

(2) event_source_manager_dispatch 函数:处理输入设备信息。

(3) main_loop_dispatch_events 函数:处理定时器和 idle 等消息源。

(4) window_manager_check_and_layout 函数:完成 UI 布局的相关操作。

(6) window_manager_paint 函数:深度遍历树状结构的 UI 树,并且把相关的 UI 控件绘制到屏幕上面,早画的控件会被晚画的控件覆盖。

在 UI 树中,窗口管理器(window_manager)是树顶,窗口管理器下可以有很多窗口,每个窗口下可以有很多控件。

5、tk_quit 函数

当程序调用 tk_quit 函数后,就会退出 AWTK 的主循环,并释放所有 AWTK 相关的资源。

6、application_exit 函数

application_exit 函数一般用于退出应用程序,该函数通常是给用户释放应用程序相关的资源。

1.4 AWTK 代码目录结构

了解 AWTK 在嵌入式下的核心代码以及相关功能模块有利于项目的移植和调试,AWTK 代码目录结构详见下表:

文件/目录说明备注
src/awtk_global.c主要负责 AWTK 的初始化以及退出 AWTK 的相关操作
src/baseAWTK 底层最基础的逻辑代码
src/blendAWTK 颜色融合的算法代码
src/clip_board剪切板模块
src/dialog_highlighters对话框高亮模块
src/ext_widgets扩展控件模块
src/font_loader字体解码器的代码显示字体时使用
src/graphic_buffer显示 buffer 对象的代码
src/image_loader图片解码器的代码解码图片时使用
src/input_engines输入法引擎模块
src/input_methods输入法模块
src/layouters布局模块
src/lcdAWTK 内置的 LCD 实现代码
src/main_loop平台相关的消息循环机制代码
src/native_window平台相关的物理窗口的实现代码
src/platforms通用平台下多线程和文件系统等与平台相关的代码
src/tkcAWTK 提供的通用函数库
src/ui_loader解析 AWTK 的 UI 文件的代码
src/vgcanvas矢量画布模块
src/widget_animators控件动画模块
src/widgets基础控件的代码
src/window_animators窗口动画模块
src/window_manager窗口管理器的代码
3rd/aggeAGGE 矢量画布引擎的代码,AWTK 默认使用该引擎支持 AGGE 矢量画布时使用
3rd/gladWindows 的 OpenGL 转接层的代码支持 OpenGL 使用
3rd/gpinyin谷歌拼音输入法的代码
3rd/libunibreakUNICODE 换行算法代码
3rd/nanovgnanovg 库,AWTK 默认使用该引擎,通常与 AGGE 配合使用支持矢量画布时使用
3rd/stbstb 库,AWTK 默认使用该引擎,实现图片/字体的解码功能解码图片时使用

AWTK 的代码主要放在 awtk/src 目录以及 awtk/3rd 目录,其中 3rd 目录主要用于存放第三方的类库。

上面提到的所有文件夹和文件都可以直接添加到移植项目中,只需要配置好相关的宏就可以使用了,功能模块相关的宏定义详见本文附录二,但是有些模块需要注意:

  1. 需要确定程序是基于 OpenGL 还是基于 Framebuffer 开发,如果基于 Framebuffer 开发,需要添加 lcd_mem_xxx 的所有文件和 native_window_raw.c 文件,如果基于 OpenGL 开发,就需要添加 lcd_nanovg.c 和 native_window_fb_gl.c 文件。
  2. 如果是裸系统平台,一般会使用 main_loop_raw.inc 文件实现 AWTK 主循环,而在嵌入式 Linux 平台一般会使用 main_loop_simple.c 文件。
  3. platforms 文件夹下提供了一些常见平台的平台相关的移植模块,可以根据具体平台选择性添加。
  4. 3rd/nanovg 文件夹中分别提供了 agge、gl 和 base 的相关代码,如果支持矢量画布功能,则 base 中的代码必须全部添加到项目中,并且基于 OpenGL 开发的程序需要添加 gl 相关代码,而基于 Framebuffer 开发的程序需要添加 agge 相关代码。
  5. 在移植的过程中,添加头文件路径,awtk 的话只需要增加 awtk/src 和 awtk/src/ext_widgets 的路径即可,如果使用到 3rd 的功能模块的话,还需要添加相关的 3rd 文件夹的路径(例如:使用到 AGGE 矢量画布的话,就需要添加 awtk/3rd/agge,awtk/3rd/nanovg 和 awtk/3rd/nanovg/base 目录)。

此处主要列出了 AWTK 代码结构并提到了一些添加文件时的注意事项,如果读者想要了解更详细的构建移植工程的过程,可以参考 STM32H743 移植笔记 以及 NXP LPC1768 移植笔记,里面详细记录了 Keil 工程的构建过程。

1.5 官方移植案例

AWTK 提供了部分常见平台和操作系统的移植案例,如 PC Windows、PC Linux、嵌入式Linux、嵌入式无操作系统平台等,用户可以直接使用或参考这些工程做实际开发。详情请浏览《AWTK生态共建计划》文档。