前言
这段时间学习了一下MiniGUI中的事件传递机制,包括从内核驱动上报事件到MiniGUI分发事件这一整条通路,在嵌入式平台上,获取触摸事件可以使用tslib框架,通过tslib获取到事件之后,会把消息保存在桌面消息队列中,然后进一步分发到应用消息队列,我会在这篇文章中详细说明
1. IAL初始化流程
IAL全拼是Input Abstract Layer,也就是输入抽象层,在MiniGUI中抽象了很多输入设备,比如tslibial、ipaqh3600ial、consoleial和nexusial等等,本文以tslibial为例进行说明
首先看一下初始化流程图,有一个初步的印象
整个MiniGUI的初始化会调用到InitGUI()函数中,本文主要关注IAL初始化相关的代码
/* libminigui-gpl-3.2/src/kernel/init.c */
int GUIAPI InitGUI (int args, const char *agr[])
{
/*...*/
/*初始化应用窗口默认事件处理进程*/
__mg_def_proc[0] = PreDefMainWinProc;
/*初始化应用对话框默认事件处理进程*/
__mg_def_proc[1] = PreDefDialogProc;
/*初始化应用控件窗口默认事件处理进程*/
__mg_def_proc[2] = PreDefControlProc;
/*...*/
/*初始化输入引擎*/
if(!mg_InitLWEvent()) {
fprintf(stderr, "KERNEL>InitGUI: Low level event initialization failure!\n");
goto failure1;
}
/*...*/
/*创建桌面接受消息线程*/
if (!SystemThreads()) {
fprintf (stderr, "KERNEL>InitGUI: Init system threads failure!\n");
goto failure;
}
/*...*/
}
static BOOL SystemThreads(void)
{
/*...*/
/*这里以MiniGUI-Threads模式为例*/
/*创建桌面接受消息线程,会初始化桌面消息队列和一个while循环去取消息*/
pthread_create (&__mg_desktop, NULL, DesktopMain, &wait);
/*...*/
/*创建接受内核上报事件线程,会等待内核事件,如果有事件则解析并上报*/
pthread_create (&__mg_parsor, NULL, EventLoop, &wait);
/*...*/
}
/* libminigui-gpl-3.2/src/kernel/event.c */
BOOL mg_InitLWEvent (void)
{
/*获取双击最小事件间隔,可以配置MiniGUI.cfg中的dblclicktime字段,默认300*/
GetDblclickTime ();
/*获取超时时间,可以配置MiniGUI.cfg中的timeoutusec字段,默认300000,该值是每一次循环获取事件的最大等待时间*/
GetTimeout ();
/*初始化具体的输入引擎*/
if (mg_InitIAL ())
return FALSE;
/*重置鼠标按键状态*/
ResetMouseEvent();
ResetKeyEvent();
return TRUE;
}
/* libminigui-gpl-3.2/src/ial/ial.c */
int mg_InitIAL (void)
{
/*...*/
/*会解析MiniGUI.cfg中的ial_engine、mdev和mtype字段,判断使用哪种输入引擎*/
/*IAL_InitInput会最终调用到具体的输入引擎中,tslibial会调用到InitTSLibInput函数*/
if (!IAL_InitInput (__mg_cur_input, mdev, mtype)) {
fprintf (stderr, "IAL: Init IAL engine failure.\n");
return ERR_INPUT_ENGINE;
}
/*...*/
}
上面写到会创建桌面接受消息线程,因为用的是MiniGUI-Threads模式,所以在desktop-ths.c中
/* libminigui-gpl-3.2/src/kernel/desktop-ths.c */
void* DesktopMain (void* data)
{
/*...*/
/*__mg_dsk_msg_queue就是桌面消息队列了,默认DEF_MSGQUEUE_LEN是16,也就是最多保存16个消息*/
if (!(__mg_dsk_msg_queue = mg_InitMsgQueueThisThread ()) ) {
_MG_PRINTF ("KERNEL>Desktop: mg_InitMsgQueueThisThread failure!\n");
return NULL;
}
/*...*/
/*开启一个while循环,调用GetMessage,指定HWND_DESKTOP,每次从__mg_dsk_msg_queue队列中取一个消息*/
while (GetMessage(&Msg, HWND_DESKTOP)) {
/*...*/
/*如果取到消息的话,就把消息丢给桌面事件处理函数进行分发,具体怎么分发,下节详解*/
lRet = DesktopWinProc (HWND_DESKTOP,
Msg.message, Msg.wParam, Msg.lParam);
/*...*/
}
/*...*/
}
2. 内核事件上报流程
在初始化的时候,开启了一个接受并解析内核事件的线程EventLoop,这一小节重点分析改函数,先看图
EventLoop函数中,主要是一个while循环,IAL_WaitEvent会调用到输入引擎的wait_event函数中去等待内核事件
/* libminigui-gpl-3.2/src/kernel/init.c */
static void* EventLoop (void* data)
{
LWEVENT lwe;
int event;
lwe.data.me.x = 0; lwe.data.me.y = 0;
sem_post ((sem_t*)data);
while (__mg_quiting_stage > _MG_QUITING_STAGE_EVENT) {
/*等待内核上报事件,__mg_event_timeout是等待超时时间,在超时时间内没有事件的话会再次进入while循环*/
event = IAL_WaitEvent (IAL_MOUSEEVENT | IAL_KEYEVENT