运行环境:
- 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;
// #endifcase 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