黑暗模式
5. 输入设备适配
5.1 输入设备适配的基本步骤
在 AWTK 中,主循环(main_loop)主要负责维持事件分发和界面绘制这个不断循环的过程,而输入设备移植的本质就是捕获系统的触摸、鼠标、键盘等输入事件,再分发给 GUI 处理,各类输入设备移植的步骤基本相同,可总结为以下三步:
- 通过设备驱动获取相应的设备事件;
- 将设备事件转化为 AWTK 支持的事件;
- 向 GUI 主循环(main_loop)分发转化后的事件。
5.1.1 捕获系统输入事件
不同平台上捕获系统输入设备事件的方法都不一样,具体需要根据实际平台上的驱动实现,此处不做赘述。
5.1.2 AWTK 支持的输入事件
目前 AWTK 支持的输入设备事件详见下表,只要能将获取到的设备事件转化为下表中的事件,那么移植就没有太大问题,各个事件转化时需要设置的参数详见下文的示例教程:
设备事件类型 | 事件名称 | 常用设备 |
---|---|---|
wheel_event_t | 鼠标滚轮事件 | 鼠标 |
pointer_event_t | 指针事件 | 鼠标、触摸屏 |
key_event_t | 按键事件 | 键盘、特殊按键设备(比如3/5按键) |
multi_gesture_event_t | 多点触摸手势事件 | 支持多点触摸的触摸屏 |
以上设备事件包含的具体信息以及它们支持的事件类型详见:awtk/src/base/events.h。
5.1.3 分发输入设备事件
捕获到系统设备事件并将其转化为 AWTK 支持的事件后,可以调用 AWTK 提供的以下接口将事件分发到 GUI 的主循环中处理:
函数名称 | 说明 | 备注 |
---|---|---|
main_loop_queue_event | 分发事件请求 | 可分发 AWTK 中的任意事件,需提供事件请求结构体 |
main_loop_post_key_event | 分发按键事件 | 需提供键值与按键状态 |
main_loop_post_pointer_event | 分发指针事件 | 需提供指针坐标与指针状态 |
main_loop_post_multi_gesture_event | 分发多点触摸事件 | 需提供多点触摸事件结构体 |
如果在非 GUI 线程中调用以上接口分发事件,那么必须参考本文第五章的内容适配互斥锁。
5.1.4 分发输入设备事件的方式
常见的输入设备事件分发方式有两种,分别是在 GUI 线程中分发以及在非 GUI 线程中分发,AWTK 为这两种方式分别提供了简单的实现,以方便用户移植。
1、在 GUI 线程中分发事件
对于裸系统平台,AWTK 提供了 main_loop_raw.inc,该文件实现了裸系统 main_loop 的基本功能,在移植时用户只需实现负责输入设备事件的分发 platform_disaptch_input 函数即可,该函数在 GUI 主循环中被调用,会阻塞主线程,具体的流程详见本文 1.3 章节,示例代码如下:
c
/* 分发输入设备事件 */
ret_t platform_disaptch_input(main_loop_t *l) {
/* 捕获系统的触摸、鼠标、键盘等输入事件,并转化为 AWTK 支持的事件 */
event_queue_req_t req; /* AWTK 的事件请求 */
memset(&req, 0x00, sizeof(req));
req.xxx = system_get_input_event();
...
/* 调用本文上一小节中介绍的接口将事件分发到 GUI 主循环 */
main_loop_queue_event(l, &req);
return RET_OK;
}
#include "main_loop/main_loop_raw.inc"
2、在非 GUI 线程中分发事件
在非 GUI 线程中获取并分发设备事件,不会阻塞主线程,可以在一定程度上提高 GUI 刷新效率。示例代码如下:
c
static void* input_run(void* ctx) {
main_loop_t* loop = (main_loop_t*)ctx;
while(1) {
/* 捕获系统的触摸、鼠标、键盘等输入事件,并转化为 AWTK 支持的事件 */
event_queue_req_t req; /* AWTK 的事件请求 */
memset(&req, 0x00, sizeof(req));
req.xxx = system_get_input_event();
...
/* 调用本文上一小节的接口将事件分发到 GUI 主循环 */
main_loop_queue_event(loop, &req);
}
return NULL;
}
ret_t platform_disaptch_input(main_loop_t *l) {
if (run_once) { /* 保证仅执行一次,创建事件分发线程 */
system_create_thread(input_run);
}
}
#include "main_loop/main_loop_raw.inc"
需要注意的是,本章节中的示例代码主要描述输入设备移植的步骤逻辑,具体的实现代码需要根据平台实际的设备驱动来编写,读者不必过于纠结示例代码中的实现细节。
5.2 鼠标移植教程
事件 | event_type_t标识 | 事件类型及参数 |
---|---|---|
鼠标左键按下 | EVT_POINTER_DOWN | pointer_event_t |
鼠标左键弹起 | EVT_POINTER_UP | pointer_event_t |
鼠标右键弹起 | EVT_CONTEXT_MENU | pointer_event_t |
鼠标移动 | EVT_POINTER_MOVE | pointer_event_t |
鼠标滚轮 | EVT_WHEEL | wheel_event_t |
鼠标中键按下 | EVT_KEY_DOWN | key_event_t,TK_KEY_WHEEL |
鼠标中键弹起 | EVT_KEY_UP | key_event_t,TK_KEY_WHEEL |
移植鼠标设备时,用户只要获取鼠标设备信息将其转化为 AWTK 的事件,并调用 main_loop_queue_event 接口将事件分发到 main_loop 中即可,代码如下:
c
/* 获取鼠标设备信息,将其转化为 AWTK 指针事件,并分发到 main_loop 中 */
void input_dispatch_mouse(/*...*/) {
event_queue_req_t req; /* AWTK 的事件请求 */
memset(&req, 0x00, sizeof(req));
/* 获取鼠标设备信息(请根据实际驱动实现) */
evt = system_get_mouse_event();
/* 将鼠标信息转化为 AWTK 的指针事件,此处以指针抬起为例 */
req.event.type = EVT_POINTER_UP; /* 设置事件类型 */
req.pointer_event.pressed = evt.pressed; /* 设置是否按压 */
req.pointer_event.x = evt.x; /* 鼠标指针的 x 坐标 */
req.pointer_event.y = evt.y; /* 鼠标指针的 y 坐标 */
...
/* 设置事件结构大小为 pointer_event_t,其他事件类似 */
req.event.size = sizeof(req.pointer_event);
/* 向主循环分发一个事件 */
main_loop_queue_event(main_loop(), &req);
}
5.3 触摸移植教程
事件 | event_type_t标识 | 事件类型及参数 |
---|---|---|
触摸按下 | EVT_POINTER_DOWN | pointer_event_t |
触摸弹起 | EVT_POINTER_UP | pointer_event_t |
触摸拖动 | EVT_POINTER_MOVE | pointer_event_t |
触摸设备的移植与鼠标设备类似,代码如下:
c
/* 获取触摸屏设备信息,将其转化为 AWTK 指针事件,并分发到 main_loop 中 */
void input_dispatch_touch(/*...*/) {
event_queue_req_t req /* AWTK 的事件请求 */
memset(&req, 0x00, sizeof(req));
/* 获取触摸屏设备信息(请根据实际驱动实现) */
evt = system_get_touch_event();
/* 将触摸屏信息转化为 AWTK 的指针事件,此处以手指抬起为例 */
req.event.type = EVT_POINTER_UP; /* 设置事件类型 */
req.pointer_event.pressed = evt.pressed; /* 设置是否按压 */
req.pointer_event.x = evt.x; /* 触摸的 x 坐标 */
req.pointer_event.y = evt.y; /* 触摸的 y 坐标 */
...
/* 设置事件结构大小为 pointer_event_t,其他事件类似 */
req.event.size = sizeof(req.pointer_event);
/* 向主循环分发一个事件 */
main_loop_queue_event(main_loop(), &req);
}
5.4 键盘移植教程
事件 | event_type_t标识 | 事件类型及参数 |
---|---|---|
按键按下 | EVT_KEY_DOWN | key_event_t |
按键弹起 | EVT_KEY_UP | key_event_t |
触摸设备的移植同样与鼠标设备类似,区别在于获取键盘按键扫描码后,要将其转化成 AWTK 的 key_code_t 代码,代码如下:
c
/* 获取键盘设备信息,将其转化为 AWTK 按键事件,并分发到 main_loop 中 */
void input_dispatch_keyboard(/*...*/) {
event_queue_req_t req /* AWTK 的事件请求 */
memset(&req, 0x00, sizeof(req));
/* 获取键盘设备信息(请根据实际驱动实现) */
evt = system_get_key_event();
key_code = system_key_to_key_code(evt.key);
/* 将键盘信息转化为 AWTK 的按键事件,此处以某键按下为例 */
req.event.type = EVT_KEY_DOWN; /* 设置事件类型 */
req.key_event.key = key_code; /* 设置键值TK_KEY_xxx,AWTK的键值请参考 awtk/src/base/keys.h */
...
/* 设置事件结构大小为 key_event_t,其他事件类似 */
req.event.size = sizeof(req.key_event);
/* 向主循环分发一个事件 */
main_loop_queue_event(main_loop(), &req);
}
5.5 其他设备移植教程
根据以上章节中鼠标、触摸屏和键盘设备的移植教程,可以发现各类输入设备的移植流程大同小异,其核心步骤都是获取设备事件将其转化为 AWTK 支持的事件并调用 main_loop_queue_event 接口分发到 main_loop 中,区别只在于设备事件的获取与转化。
某些嵌入式平台会提供一些特殊的输入设备,比如 STM32f103ze 平台提供四个特殊按键,分别表示上、下、左、右,常用于 GUI 界面上切换控件焦点,与移植键盘设备类似,实现代码如下:
c
/* 获取特殊按键设备信息,将其转化为 AWTK 按键事件,并分发到 main_loop 中 */
void input_dispatch_4key(/*...*/) {
bool is_press = FLASE; /* 默认按键为抬起状态 */
/* 获取键盘设备信息(请根据实际驱动实现) */
int key = KEY_Scan(0);
/* 获取键值和按键状态,AWTK 的键值请参考 awtk/src/base/keys.h */
input_dispatch_get_key_value_and_state(&key, &is_press);
/* 向主循环分发一个按键事件 */
main_loop_post_key_event(main_loop(), is_press, key);
}