MiniGUI移植的核心所在就是GAL和IAL的移植。在嵌入式linux应用中,内核通常会提供对framebuffer设备的驱动,而在GAL中对于fb的支持,MiniGUI是默认的。因此,通过简单的MiniGUI.cfg配置ial=fbcon,就可以完成GAL的支持了。
对IAL的支持并不是那么容易。Linux对输入设备目前还没有一个统一的标准,也未形成任何规范。虽然keyboard在桌面领域已占据主导地位,在嵌入式中不可能使用类似于键盘那么复杂的设备。嵌入式中多使用触摸屏和4×4的小键盘,而这类设备虽然表面雷同,功能键的定义千差万别。移植这类设备的IAL实现更有意义。 MiniGUI不论是库还是应用程序都不是内核的一部分,因此,MiniGUI没有直接访问设备的权限。为了实现触摸屏和小键盘的支持,内核中必须提供这二者的驱动程序。当然,这里的驱动程序可以是最底层的实现,更为复杂的通过MiniGUI的IAL驱动扩展实现。
因此在MiniGUI中实现一种新设备的IAL支持,通常提供如下步骤:
1.内核提供该设备(触摸屏或者小键盘)的驱动程序。
2.在libminigui的src/ial/目录建立需要的.c和.h文件,编写高层IAL驱动程序。
3.将新的IAL驱动程序编译到MiniGUI的库中,以便为应用程序提供新的IAL支持。
4.修改MiniGUI.cfg的ial配置,使应用程序运行时使用移植好的IAL设备。
实现一个输入引擎,可以将触摸屏按照鼠标处理,将按键按照键盘处理。
如何在MiniGUI中添加新的IAL引擎:(假设新的IAL引擎为_NAME_IAL)
1. 在ial.c文件中添加新引擎的入口:
例如:(ial.c文件中)
A) #ifdef _NAME _IAL
#include "NAME.h"
#endif
B) 在input数组中添加
#ifdef _NAME _IAL
{"NAME ", InitNAMEInput, TermNAMEInput},
#endif
2. 把新的 .c 添加到 Makefile.am 文件中即可。
3. 修改配置文件IAL引擎项,使用这个新的IAL引擎_NAME_IAL.
² 如何编写IAL引擎:
1. 在MiniGUI中引入了输入抽象层(Input Abstract Layer ,IAL)的概念,它大大提高了MiniGUI的可移植性。
2. IAL是定义的一组不依赖于任何特殊硬件的抽象接口。而我们这里所说的IAL引擎则是对IAL定义的抽象接口的实现的底层代码。
3. IAL接口对下是IAL引擎,这些引擎使用了输入设备驱动提供的一些函数;对上则是应用程序或是GDI,上层MiniGUI库中ParseEvent()函数将通过这些IAL引擎将收集到的鼠标键盘事件转换为MiniGUI中的消息。
4. MiniGUI IAL结构如下:
在代码实现上,MiniGUI通过INPUT数据结构来表示输入引擎
typedef struct tagINPUT
{
char* id;
// Initialization and termination
BOOL (*init_input) (struct tagINPUT *input, const char* mdev, const char* mtype);
void (*term_input) (void);
// Mouse operations
int (*update_mouse) (void);
void (*get_mouse_xy) (int* x, int* y);
void (*set_mouse_xy) (int x, int y);
int (*get_mouse_button) (void);
void (*set_mouse_range) (int minx, int miny, int maxx, int maxy);
void (*suspend_mouse) (void);
int (*resume_mouse) (void);
// Keyboard operations
int (*update_keyboard) (void);
const char* (*get_keyboard_state) (void);
void (*suspend_keyboard) (void);
int (*resume_keyboard) (void);
void (*set_leds) (unsigned int leds);
// Event
#ifdef _LITE_VERSION
int (*wait_event) (int which, int maxfd, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout);
#else
int (*wait_event) (int which, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout);
#endif
char mdev [MAX_PATH + 1];
}INPUT;
extern INPUT* cur_input;
系统启动后,将根据配置文件寻找特定的输入引擎作为当前的输入引擎,并对全局变量cur_input(表当前使用的输入引擎)赋值。
为书写方便,定义了当前引擎的C语言宏。
#define IAL_InitInput (*cur_input->init_input)
#define IAL_TermInput (*cur_input->term_input)
#define IAL_UpdateMouse (*cur_input->update_mouse)
#define IAL_GetMouseXY (*cur_input->get_mouse_xy)
#define IAL_GetMouseButton (*cur_input->get_mouse_button)
#define IAL_SetMouseXY if (cur_input->set_mouse_xy) (*cur_input->set_mouse_xy)
#define IAL_SetMouseRange if (cur_input->set_mouse_range) (*cur_input->set_mouse_range)
#define IAL_SuspendMouse if (cur_input->suspend_mouse) (*cur_input->suspend_mouse)
#define IAL_UpdateKeyboard (*cur_input->update_keyboard)
#define IAL_GetKeyboardState (*cur_input->get_keyboard_state)
#define IAL_SuspendKeyboard if (cur_input->suspend_keyboard) (*cur_input->suspend_keyboard)
#define IAL_SetLeds(leds) if (cur_input->set_leds) (*cur_input->set_leds) (leds)
5. 我们编写IAL引擎要做的就是在_NAME_IAL.c中实现输入引擎结构中定义的这些函数。如:_NAME_IAL.c:
#include ………
…………..变量定义
/************************ Low Level Input Operations **********************/
mouse_update()
{
……………
}
mouse_getxy()
{
………………..
}
mouse_getbutton()
{
………………..
}
………….IAL引擎结构中定义的其它有关mouse的函数一般无需实现。
Keyboard_update()
{
……………..
}
keyboard_getstate()
{
………
}
……………..同样结构中定义的其它有关keyboard的函数一般无需实现。
Wait_event()
{
………………..
}
InitNAMEInput(INPUT* input, const char* mdev, const char* mtype)
{
…………
input->update_mouse = mouse_update;
input->get_mouse_xy = mouse_getxy;
input->set_mouse_xy = NULL;
input->get_mouse_button = mouse_getbutton;
input->set_mouse_range = NULL;
input->update_keyboard = keyboard_update;
input->get_keyboard_state = keyboard_getstate;
input->set_leds = NULL;
input->wait_event = wait_event;
………………..
}
void TermNAMEInput (void)
{
…………….
}
6. 各函数功能说明:开发一输入引擎一般也就是实现以下几个函数。
InitNAMEInput函数就是在src/ial/ial.c中定义的NAME输入引擎的初始化函数,它打开触摸屏(鼠标)和键盘设备文件。在成功打开这两个设备文件后,该函数设置了INPUT结构的其它一些成员。其中一些成员被赋值为NULL。
Mouse_update函数始终返回1,表明更新鼠标状态成功。
Mouse_getxy函数返回由其它函数准备好的鼠标位置,有可能做了适当的边界检查和支持屏幕显示旋转时对坐标的转换。函数的参数是两个指针变量。
Mouse_getbutton函数返回了触摸屏状态,即用户是否触摸了屏幕,相当于是否按下了左键。(或者鼠标哪个键按下,左键、右键还是当中的那个?)
返回值为触摸屏(鼠标)状态。
Keyboard_update函数根据其它函数准备好的键盘消息,适当填充了state数组。返回值是NR_KEYS。
Keyboard_state函数直接返回了state数组的地址:“return state”。
Wait_event函数是输入引擎的核心函数。这个函数首先将先前打开的两个设备的文件描述符与传入的in文件描述符集合并在了一起,然后调用了select系统调用。当select系统调用返回大于0的值时,该函数检查在两个文件描述符上是否有可读的数据等待读取,如果是,则分别从两个文件描述符读取触摸屏和按键数据。
返回值int型retvalue变量,其中包含了信息:鼠标事件发生、键盘事件发生或者鼠标和键盘事件都发生了(retvalue |= IAL_MOUSEEVENT,retvalue |= IAL_KEYEVENT)。
7. 开发IAL引擎,实现以上函数:
开发IAL引擎准备工作:需对输入设备有些了解:
a) 是鼠标、触摸屏还是触摸板?键盘都有哪些键?
b) 数据包格式怎样?(同样是触摸屏,不同型号其通过设备文件获得数据结构也不一样,得到的若是A/D转换得来的原始数据处理起来就有些麻烦;键盘的释放与按下状态是通过判断读得的字符最高位0与1来判断,还是对同一键其按下和释放对应两个毫无联系的字符)
c) 键盘的编码怎样,和MiniGUI在include/common.h中定义的一样吗?
d) 在读得的坐标和屏幕显示坐标间需不需要进行坐标转换?Mouse_getxy中最终返回的坐标为屏幕显示坐标。
e) 输入设备驱动支持select系统调用吗?
f) 触摸屏(触摸板)驱动中是否解决了抖动消除的问题,是否需要在IAL引擎中解决触摸屏消抖?
总结代码中几个针对具体系统开发的IAL 引擎:
a) 引擎包含的各个函数所要做的工作在上面的函数功能说明中已经反应出来,但是具体处理起来,不一定要求有的工作就得在某个函数中完成。比如,从设备文件读取鼠标坐标的信息并对其进行处理,不一定都在Mouse_getxy中完成,可以在Wait_event或update_mouse中完成,在mouse_getxy中只是将最终的显示屏幕坐标赋给mouse_getxy函数的两个参数。
b) IAL引擎的作用是正确分析从设备文件读得的数据,获得鼠标(触摸屏、触摸板)坐标和状态及键盘按键情况(哪个键按下?该键是不是释放了?).这也是我们开发IAL引擎的指南。
c) IAL引擎在MiniGUI的使用(位置):
在/src/sever/server.c L396 函数IdleHandler4Server()中用了输入引擎中的IAL_WaitEvent检查是否有底层输入事件发生,
当有事件发生时检查是鼠标(触摸屏、触摸板)事件发生还是键盘事件发生,并分别用parseEvent(msg_que,event)函数(/src/sever/server.c)处理这些事件
ParseEvent (msg_queue, IAL_MOUSEEVENT)
ParseEvent (msg_queue, IAL_KEYEVENT)
ParseEvent (msg_queue, 0)
parseEvent(msg_que,event)函数中首先调用了Getlwevent(event,&lwe) (lwe是在parseEvent中定义的),Getlwevent(event,&lwe)中分析event:
当event是IAL_KEYEVENT时:
调用了引擎IAL_UpdateKeyboard ()和IAL_GetKeyboardState ()
当event是IAL_MOUSEEVENT时:
调用了引擎IAL_UpdateMouse ()和函数RefreshCursor(&me->x, &me->y, &button)(该函数中调用了IAL_GetMouseXY (x, y)和IAL_GetMouseButton )
Getlwevent(event,&lwe)最终得到了鼠标键盘的坐标状态等具体信息,这些信息由lwe变量带回。
变量Lwe数据结构:
typedef struct _LWEVENT
{
int type;
int count;
DWORD status;
LWEVENTDATA data;
}LWEVENT;
typedef union _LWEVENTDATA {
MOUSEEVENT me;
KEYEVENT ke;
}LWEVENTDATA;
parseEvent(msg_que,event)中利用了获得的lwe,将其转化为消息,放到消息队列中。如对键盘:
Msg.message=Msg-KEYDOWN
Msg.wparam=ke->scancode
Msg.iparam=ke->status
Getlwevent(event,&lwe)收集底层输入事件lwe(利用了IAL引擎)
parseEvent(msg_que,event)将收集到的这些事件转化为上层能理解的消息。
d) 有关键盘的IAL引擎就是给state数组赋值。
MiniGUI在include/common.h中列举了每种按键在state数组中的位置(如#define SCANCODE_Z 44可以知道state数组中第44个元素表示Z键的状态),我们必须分析从键盘配置文件读取的字符字节的信息,根据这些信息给state数组中相应元素赋值,这样MiniGUI才能根据state数组正确处理这些事件(即MiniGUI是通过state数组中的元素来正确识别哪个键按下的。必须严格根据state数组的规定)。
8. 编写输入引擎可参考ipaq.c,它是针对了比较普通的触摸屏的写的。
9. 写出触摸屏接口代码,注意:鼠标获取的数据一般是相对坐标,而典型的触摸屏则是绝对坐标(差劲的驱动是直接返回A/D转换得来的原始数据)。