运行环境:
- Windows 10
- LVGL 7.5.0
- SDL2
- gcc
LVGL模拟器内已经基于SDL2做了一个基础的键盘输入驱动,先简单叙述下如何使用吧。假设有一个text area,需要基于PC键盘实现文本的基本控制(增、删、移动光标)。值得庆幸的是模拟器内已经做好了框架,在初步跑起来之前需要对 lv_drivers\indev\keyboard.c 文件进行调整。
lv_drivers\indev\keyboard.c
lv_drivers\indev\keyboard.c 文件内 LV_KEY_BACKSPACE 和 LV_KEY_DEL 被宏控制,需要取消宏,否则text area内将无法使用退格键和删除键,修改后如下:
static uint32_t keycode_to_ascii(uint32_t sdl_key)
{
/*Remap some key to LV_KEY_... to manage groups*/
switch(sdl_key) {
case SDLK_RIGHT:
case SDLK_KP_PLUS:
return LV_KEY_RIGHT;
case SDLK_LEFT:
case SDLK_KP_MINUS:
return LV_KEY_LEFT;
case SDLK_UP:
return LV_KEY_UP;
case SDLK_DOWN:
return LV_KEY_DOWN;
case SDLK_ESCAPE:
return LV_KEY_ESC;
// #ifdef LV_KEY_BACKSPACE /*For backward compatibility*/
case SDLK_BACKSPACE:
return LV_KEY_BACKSPACE;
// #endif
// #ifdef LV_KEY_DEL /*For backward compatibility*/
case SDLK_DELETE:
return LV_KEY_DEL;
// #endif
case SDLK_KP_ENTER:
case '\r':
return LV_KEY_ENTER;
default:
return sdl_key;
}
}
LV_KEY_* 键码定义在 lvgl\src\lv_core\lv_group.h 文件内。keycode_to_ascii 函数的作用就是将SDL2内定义的按键码转换成LVGL内定义的按键码,如果需要在Windows里面基于LVGL写一些小工具或者小软件,又需要使用键盘输入的话,直接在这个函数里面改就行,还是很方便的。
LVGL V7版本的文档内对绑定键盘输入的叙述如下:
照猫画虎吧,创建一个text area,再创建一个组,最后做一个绑定:
extern lv_indev_t *keyboard_indev;
static lv_obj_t * text_recv = NULL;
static lv_group_t * g = NULL;
void text(lv_obj_t *obj)
{
text_recv = lv_textarea_create(obj, NULL);
lv_obj_set_size(text_recv, 50, 30);
lv_textarea_set_text_sel(text_recv, true);
lv_textarea_add_text(text_recv, "hi");
lv_obj_set_event_cb(text_recv, event_handler_text_recv);
g = lv_group_create();
lv_group_add_obj(g, text_recv);
lv_indev_set_group(keyboard_indev, g);
}
static void event_handler_text_recv(lv_obj_t *obj, lv_event_t event)
{
lv_indev_data_t data;
if(event == LV_EVENT_KEY)
{
printf("LV_EVENT_KEY\n");
keyboard_indev->driver.read_cb(keyboard_indev, &data);
printf("key=%d\n", data.key);
}
}
变量 keyboard_indev 存储的是函数 lv_indev_drv_register() 的返回值。在LVGL提供的模拟器工程中,鼠标键盘的初始化被放置在 main.c 的 hal_init() 函数内,但是只做了鼠标注册后返回句柄的存储,键盘的句柄被忽略了,此处需要自己加上,修改后 hal_init() 部分代码如下:
/* Add the keyboard as input device
* Use the 'keyboard' driver which reads the PC's keyboard*/
keyboard_init();
lv_indev_drv_init(&keyb_drv);
keyb_drv.type = LV_INDEV_TYPE_KEYPAD;
keyb_drv.read_cb = keyboard_read;
/* */
keyboard_indev = lv_indev_drv_register(&keyb_drv);
编译运行后发现模拟器内的键盘功能只能说堪堪能用,至少小写字母、数字、删除键、退格键以及回车这些都能用,大写字母和需要Shift组合输入的字符就不行了,这个得自己去 keycode_to_ascii 函数内处理。
lv_drivers\indev\keyboard.c 修改了一部分,基本满足目前的需求~
/**
* @file sdl_kb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "keyboard.h"
#if USE_KEYBOARD
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static uint32_t keycode_to_ascii(uint32_t sdl_key);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t last_key;
static lv_indev_state_t state;
static uint32_t last_mod;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the keyboard
*/
void keyboard_init(void)
{
/*Nothing to init*/
}
/**
* Get the last pressed or released character from the PC's keyboard
* @param indev_drv pointer to the related input device driver
* @param data store the read data here
* @return false: because the points are not buffered, so no more data to be read
*/
bool keyboard_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
(void)indev_drv; /*Unused*/
data->state = state;
data->key = keycode_to_ascii(last_key);
return false;
}
/**
* It is called periodically from the SDL thread to check a key is pressed/released
* @param event describes the event
*/
void keyboard_handler(SDL_Event *event)
{
/* We only care about SDL_KEYDOWN and SDL_KEYUP events */
switch (event->type)
{
case SDL_KEYDOWN: /*Button press*/
last_key = event->key.keysym.sym; /*Save the pressed key*/
last_mod = event->key.keysym.mod; /* Save current key modifiers */
/* 忽略控制按键 */
switch (last_key)
{
case SDLK_CAPSLOCK:
// 大写键
case SDLK_LCTRL:
// 左Ctrl键
case SDLK_RCTRL:
// 右Ctrl键
case SDLK_LSHIFT:
// 左Shift键
case SDLK_RSHIFT:
// 右Shift键
case SDLK_LALT:
// 左Alt键
case SDLK_RALT:
// 右Alt键
case SDLK_LGUI:
// 左Win键
case SDLK_RGUI:
// 右Win键
state = LV_INDEV_STATE_REL;
return;
default:
break;
}
state = LV_INDEV_STATE_PR; /*Save the key is pressed now*/
break;
case SDL_KEYUP: /*Button release*/
state = LV_INDEV_STATE_REL; /*Save the key is released but keep the last key*/
break;
default:
break;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert the key code LV_KEY_... "codes" or leave them if they are not control characters
* @param sdl_key the key code
* @return
*/
static uint32_t keycode_to_ascii(uint32_t sdl_key)
{
/*Remap some key to LV_KEY_... to manage groups*/
switch (sdl_key)
{
case SDLK_RIGHT:
case SDLK_KP_PLUS:
return LV_KEY_RIGHT;
case SDLK_LEFT:
case SDLK_KP_MINUS:
return LV_KEY_LEFT;
case SDLK_UP:
return LV_KEY_UP;
case SDLK_DOWN:
return LV_KEY_DOWN;
case SDLK_ESCAPE:
return LV_KEY_ESC;
/*For backward compatibility*/
case SDLK_BACKSPACE:
return LV_KEY_BACKSPACE;
/*For backward compatibility*/
case SDLK_DELETE:
return LV_KEY_DEL;
case SDLK_KP_ENTER:
case '\r':
return LV_KEY_ENTER;
default:
/* 大写键被按下 */
if (last_mod == KMOD_CAPS)
{
if ((last_key >= 'a') && (last_key <= 'z'))
{
return (last_key - 0x20);
}
}
/* shift键按下 */
if (last_mod & KMOD_SHIFT)
{
switch (last_key)
{
case '`':
return '~';
case '1':
return '!';
case '2':
return '@';
case '3':
return '#';
case '4':
return '$';
case '5':
return '%';
case '6':
return '^';
case '7':
return '&';
case '8':
return '*';
case '9':
return '(';
case '0':
return ')';
case '-':
return '_';
case '=':
return '+';
case '[':
return '{';
case ']':
return '}';
case '\\':
return '|';
case '\'':
return '"';
case ',':
return '<';
case '.':
return '>';
case '/':
return '?';
case ';':
return ':';
default:
break;
}
}
return sdl_key;
}
}
#endif