分享

UCGUI窗体管理及消息处理机制分析

 guitarhua 2012-09-20
2010-01-14 17:13

UCGUI窗体管理及消息处理机制分析

UCGUI窗体管理及消息处理机制分析

----多对话框/模态窗体/透明窗体支持分析

作者:ucgui

日期: 2005-09-08[v1.0.0.0 2005-06-30完成]

来源: http://www.

版本: v1.0.0.1

版本
修改说明
时间

v1.0.0.0
ü    实现UCGUI中多对话框支持。
2005-06-30

v1.0.0.1
ü    增加UCGUI中各种基本消息介绍 。

ü    增加窗体消息LOOP机制介绍。

ü    增加对话框结构说明及其消息LOOP处理移到M ainTask函数中由用户处理的原因剖析,并详细分析了对话框中按钮的点击消息传送到用户自定义对话框回调函数中处理的传递流程。

ü    增加外部输入设备消息处理机制介绍。如滑动操作外设MOUSE及触摸屏输入消息(WM_TOUCH)的处理机制,及按键式操作外设消息(WM_KEY)处理机制。

ü    增加一种更简单的多对话框支持的方法及说明。

ü    增加模态对话框实现原理分析。

ü    增加透明窗体实现原理分析。
2005-09-08




问题的提出:

        [求助]关于对话框处理程序中,想在OK按钮按下后想弹出一个消息框,该怎么做?直接加在程序中好像不行,如何让消息框弹出后成为模态窗体呢?请版主帮帮忙。

        [解析]在UCGUI中,对话框只支持单个对话框窗体,不支持多个独立的对话框,现在我们从其源码来分析一下它为什么支持单个对话框窗体以及如何改进它以支持多个独立对话框,要讲解这个问题我们必须首先理解UCGUI中的窗体消息LOOP,没有消息LOOP窗体就是死水一潭,不能接受任何外界的输入,只是一个画在那里的图画而已。

        [声明]本文中提到的源码均为UCGUI3.24版源码,新版UCGUI源码会有改动,请下载本文示例代码来结合阅读本文。

摘要: 本文主要介绍了UCGUI中的对话框的消息处理机制,并指出在现有UCGUI上如何增加多窗体支持,并在分析解决问题时着重介绍了其输入设备消息WM_TOUTCH及WM_KEY两类消息处理方法,并同时初步指出一种在UCGUI中实现模态对话框以及透明窗体的原理说明,不还有窗体重画消息WM_PAINT消息处理原理。

一、各种基本消息介绍及处理流程----对话框内部消息流转及外部消息LOOP分析.

UCGUI是采用的消息驱动的,它专门有对外的一套收集消息的接口, 我在模似器中, 就是通过LCD模拟显示屏窗口的MOUSE消息,将MOUSE消息传入到这个接口中, 以驱动UCGUI中的窗体的。

UCGUI中的消息驱动其实与WINDOWS的是类似的,几种基本的消息与WINDOWS是一样的,但UCGUI的更简单且消息更少,对于一些消息的处理得也很简化,没有WINDOWS那么多的消息种类及复杂处理。在WINDOWS中,如我们处理按钮控件的点击事件的是在WM_COMMAND消息中,通过按钮的标志ID来区分不同的按钮,所以按钮标志ID必须不同的,否则无法区别开(除非不在父窗体的WM_COMMAND消息中处理)。

UCGUI中一些基本的消息如下:

        WM_CREATE---窗体创建消息,每创建一个窗体完后都会向该窗体发送此消息,如WM_CreateWindowAsChild创建完窗体均会发一此消息,但在UCGUI中对于此消息的很少处理,如果用户想在对话框之后做些初始化操作或是创建其它子窗体的动作,可以处理此消息,不过对话框一般有专门的初始化消息WM_INIT_DIALOG,它是在创建对话框后发送的。

        WM_SHOW-----显示窗体消息,此消息在UCGUI中各控件窗体内均未作处理,如果你通过消息发送函数来发送这类没有在UCGUI中各窗体中处理的消息,是没有有什么响应的,不要感到奇怪。要显示窗体一般是通过WM_ShowWindow()函数实现的,这个函数做的也就是改变窗体显示标志[WM_SF_ISVIS],并使窗体矩形区域无效[WM_InvalidateWindow()]以产生重画消息。

        WM_SET_ENABLE---设置窗体不能使用消息,UCGUI中有一种复选框为不可改变的,但是这个功能也不完全,如果你对着UCGUI中的按钮使用WM_DisableWindow()来设置其无效,按钮照样还是可以使用,不过要改进这些小毛病还是很容易的,这里只是提醒大家UCGUI中很多没有实现的小地方,不要到时候使用时感到很奇怪,感觉到奇怪时最好去看看源码,看看源码中是否实现了此功能,不要郁闷。

        WM_PAINT ----窗体重画消息,当窗体所在区域全部或是部分区域无效时,系统将会发出该重画消息,将无效区域重画,但UCGUI中的处理比较简单,都是将窗体全部区域重画;如果用户自己想在窗体上画上一些信息,一般都在在该消息当中画,UCGUI中的各种提供的系统控件都必须在其系统的提供的消息回调函数中处理此消息来画出控件。当由外部输入操作引起无效窗体区域产生时,系统都会在消息处理中发送该消息到窗体消息回调函数中,以重画此窗体,在下面讲解消息循环机制时将会着重讲解到该消息的产生。

[透明窗体]---经常有朋友想知道在UCGUI中如何实现透明窗体,透明窗

窗体显示在前台时,可以看到部分位于其窗体后的内容,即透过窗体可     以看到窗体背后的图象。在UCGUI中有关于透明窗体的设置选项,可是

没有实现此功能,其实要实现原理如下:第一透明窗体及其所有子窗体

都必须透明处理;第二是对于所有有透明属性的窗体,在绘图时必须使

用透明填充功能的矩形填充函数,主要是修改窗体的WM_PAINT消息中画

窗体时的矩形填充函数为透明的矩形填充;第三透明的矩形填充函数的

实现,通常情况下的矩形填充是以当前前景色来填充,那么关键就是实

现画点函数的透明填充,要使一个透明,可以取当前显存中存点的点的

RGB颜色,然后再与当前要画的颜色按照一个比例进行混合得一个新的

RGB值,再将此值画以屏幕上就可能实现透明填充的效果。

        WM_TOUCH----处理类似MOUSE的滑动操作方式的输入外设的消息,如触摸屏一般都是将其消息从硬件接收到后转化为该消息形式发送出去,该消息中必须包含消息在屏幕中的发生位置坐标及输入设备状态(按下状态或弹起状态),此消息在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,在后面将详细讲解该消息的处理机制。

        WM_KEY------处理类似KEY的按键式操作的输入外设的消息,消息中必须包含按键的按下或弹起状态,此消息也是在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,讲解消息LOOP时再详细介绍。

        WM_SET_FOCUS----讲到刚才上面的两个消息时,就反复提到了当前焦点窗体的概念,所有外部输入设备消息都是发送给当前焦点窗体的,用户可以通过此消息来设定当前的焦点窗体。外部输入操作也会改变当前焦点窗体,如点击某窗体时会在该窗体的WM_TOUCH消息处理中设置该窗体本身为当前焦点窗体;当在对话框中按键TAB键时,同样也可以将焦点在对话框上各控件间切换,这是在对话框的WM_KEY消息中处理实现的[了解一下WM_SetFocusOnNextChild()函数],是根据创建对话框时指定的资源定义数组中的顺序来切换的,并没有WIN下面指定的TabIndex这样一个值来指定次序的值。

        WM_NOTIFY_PARENT---这个消息将子窗体的外设输入的消息传送到它的父窗体,因为一般的情况下消息都是在父窗体中统一处理的,如对话框中的按钮点击事件,一般都是在用户自定义的窗体消息处理函数中处理,所以就必须要子窗体将获取的输入外设的消息传送给父窗体,这样才能在父窗体中进行子窗体的点击事件消息的处理,这个消息的机制类似WIN下面的WM_COMMAND消息,处理该消息时通过控件ID来区别不同的控件,通过消息中的通知码来区别控件被操作的各种状态,具体这个消息的详细说明请参见后面的分析。

        WM_DELETE---要删除窗体时发送的消息,主要清除窗体数据结构所占用内存,此消息主要由WM_DeleteWindow()函数发送了,如点击OK按钮关闭对话框时,最终会调用此函数来删除窗体,不过UCGUI中没有最大化最小化关闭等系统功能按钮。最基础窗体结构注解如下,在该结构中有两个很重要的成员,hNextLin是记载窗体的下一个窗体,这个成员用于遍历所有已经创建的窗体;hNext是记载窗体下一个兄弟窗体,这个成员用于遍历每个窗体对应的子窗体;这个结构是最基础,一般的控件在这个结构之上还会有一些扩展的结构,如按钮对应有BUTTON_Obj结构。

typedef struct WM_OBJ_struct WM_Obj;

struct WM_OBJ_struct {

GUI_RECT Rect;        /* 窗体矩形区域 */

GUI_RECT InvalidRect; /* 窗体无效矩形区域 */

WM_CALLBACK* cb;      /* 窗体消息回调函数 */

WM_HWIN hNextLin;     /* 窗体下一个窗体句柄*/

WM_HWIN hParent;       /* 父窗体句柄*/

WM_HWIN hFirstChild;   /* 第一子窗体句柄*/

WM_HWIN hNext;         /* 下一个兄弟窗体句柄 */

U16 Status;           /* 窗体当前状态 */

};

      WIDGET_HandleActive()—基础控件共通消息处理,在大部分的UCGUI控件中都会在消息回调函数的头部进行这个调用,如果处理了消息后,就直接退出消息回调函数的调用。这个函数中处理如下消息:

2       WM_GET_ID[返窗体控件标志ID].

2       WM_SET_FOCUS[设置当前窗体为焦点窗体,设置完后还必须向该窗体的父窗体发送一个WM_NOTIFY_CHILD_HAS_FOCUS消息让其父窗体更新它记载的当前焦点子窗体].

2       WM_GET_HAS_FOCUS[获取当前窗体是否为焦点窗体].

2       WM_SET_ENABLE[设置窗体为不可用窗体] .

2       WM_GET_ACCEPT_FOCUS[获取当前窗体是否可设置为焦点窗体].

2       WM_GET_INSIDE_RECT[返回窗体内框矩形,如按钮有3D效果时会有效果边框宽度,内框矩形就是窗体矩形被边框剪裁后的矩形].

      WM_DefaultProc()----窗体默认消息处理函数,UCGUI中提供一些基础的控件,这些控件有些共通的消息均在此处理,如下:

2       WM_GETCLIENTRECT[获取窗体矩形区域,相对于矩形自身]

2        WM_GETORG[获取窗体矩形左上角坐标].

2       WM_GET_INSIDE_RECT[获取窗体矩形区域,相对屏幕].

2       WM_GET_CLIENT_WINDOW[获取窗体客户区子窗体句柄,如对话框的中的子窗体FrameWin即为此种窗体].

2       WM_KEY[铵键消息处理,通知父窗体子窗体的按键消息,有些控件自己要处理这个消息,如Edit控件处理完此消息后就没有再调用WM_DefaultProc(),从而没有将WM_KEY消息通父窗体;如Button控件,根本没有对此消息进行处理,直接是通过默认处理发给了父窗体处理;有些控件如Checkbox自己处理该消息,同时也调用默认消息处量将此消息通知父窗本,此种消息源窗体为子控件,目标窗体为父窗体。如此处理WM_KEY消息完全是UCGUI中如此做,在WIN中并没有这样做].

2       WM_GET_BKCOLOR[获取窗体背景色,在此未实现,返回0xfffffff值,但 FrameWin窗体实现了此消息处理].

在UCGUI的对话框的窗口消息处理函数中OK按钮的点击事件, UCGUI的处理方法与WIN下面是不同, 它在WM_NOTIFY_PARENT消息中处理[片段如下]:

    case WM_NOTIFY_PARENT:

      Id    = WM_GetId(pMsg->hWinSrc);    /* Id of widget */

      NCode = pMsg->Data.v;               /* Notification code */

      switch (NCode) {

        case WM_NOTIFICATION_RELEASED:    /* React only if released */

          if (Id == GUI_ID_OK) {          /* OK Button */

             GUI_MessageBox("This text is shown\nin a message box",

                   "Caption/Title", GUI_MESSAGEBOX_CF_MOVEABLE);

          }

          if (Id == GUI_ID_CANCEL) {      /* Cancel Button */

            GUI_EndDialog(hWin, 1);

          }

          break;

      }break;

UCGUI中的消息种类不多, 只有差不多不到二十种,但对于嵌入式系统来说已经完全足够了,用户可以自定义消息(从WM_USER起)。 WM_NOTIFY_PARENT这个消息是由子窗体传送给父窗体的, 由消息的名字也可以看出这一点,OK按钮也是一个窗体,当MOUSE点击在它上面时,UCGUI首先会传递一个WM_TOUCH消息到OK按钮的窗口消息处理函数,OK按钮是一个系统提供的控件,系统已经提供了一个默认的消息的窗口消息处理函数,这个函数会处理大部分的默认窗口消息并随后将此消息转发给父窗体,即WM_NOTIFY_PARENT消息,它是由函数WM_NotifyParent(hObj, Notification)实现的.

WM_TOUCH消息在按钮的消息处理函数_BUTTON_Callback中的_OnTouch函数中处理,在处理过程完后会调用WM_NotifyParent向按钮的父窗体发WM_NOTIFY_PARENT消息告诉对话框回调函数按钮被点击了,这个过程再说详细一点是这样的:

        点击OK按钮.

        产生按钮WM_TOUCH消息.

        UCGUI中的消息LOOP调用按钮默认的按钮窗口消息处理函数_BUTTON_Callback.

        _OnTouch默认处理按钮点击并发送给父窗体WM_NOTIFY_PARENT消息,这里要注意MOUSE点击后,有三种情况:第一种是点击后在按钮范围内弹出MOUSE,这种情况下,会送的消息中还有一个通知码就是WM_NOTIFICATION_RELEASED;第二种情况是点击拖到按钮范围外弹起MOUSE,此时通知码是WM_NOTIFICATION_MOVED_OUT;第三种情况是点击后一直未弹起MOUSE的过程中消息通知码为WM_NOTIFICATION_CLICKED;在这个函数中还会处理设置按钮点击后MOUSE至未弹起前的按下状态,这样在按钮下一次画出时就会以按下的状态显示出来.

        默认的对话框窗体消息处理函数_FRAMEWIN_Callback收到WM_NOTIFY_PARENT消息并最终传送该消息到用户自己定义的对话框消息处理函数,这里要注意的一点是,其实对话框对话框主要是由一个FrameWin子窗体构成的,这个子窗体大小为对话框指定的大小,对话框上的其它控件是都是FrameWin的子窗体,由_FRAMEWIN_Callback传送的消息首先是传送到对话框的默认窗体消息回调函数_cbDialog,然后再经它传送到用户自定义的窗体回调函数当中。

        用户在自己的对话框消息处理函数中处理WM_NOTIFY_PARENT消息,即按钮的点击消息,该消息参数中含有按钮的ID及操作状态,如果通知码是WM_NOTIFICATION_RELEASED,此时证明一次点击事件完成。

void WM_NotifyParent(WM_HWIN hWin, int Notification){

WM_MESSAGE Msg;

Msg.MsgId   = WM_NOTIFY_PARENT;

Msg.Data.v = Notification;

WM_SendToParent(hWin, &Msg);

}

这个函数相当简单, 其主要还是WM_SendToParent这个函数的调用, 这个函数再调用void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg), 这个函数是最基本的一个消息发送处理函数, 它的第一个参数指定了接受这个要处理的消息的句柄, 第二个指定了是什么消息。这个函数的主要作用是调用相应窗口的消息处理函数来处理消息,如果你有消息要发送给指定的窗体处理,那么也可以使用这个函数。

在上面, 我们刚刚分析了在对话框内部消息处理的流转,其中分析了我们在自己指定的对话框消息处理函数当中是如何可以获得按钮的点击消息并进行处理的,现在我们就再来分析一下对话框外面的消息接收:首先是来了解一下GUI_ExecDialogBox函数,这个函数有几个参数:

        第一个是对话框的资源定义数组,这个数组定义了对话框的组成子窗体,其中数组第一个成员必须是FrameWin窗体,数组每一个成员记载了创建子窗体所用函数/子窗体Caption/子窗体标志ID/子窗体的位置及宽高/创建窗体时样式标志/额外传送的参数.

        第二个参数是上述的数组的大小.

        第三个参数是用户指定的对话框窗体消息回调函数指针.

        第四个参数是对话框的父窗体,默认为0.

        第五、六参数指定对话框的左上角屏幕位置.

GUI_ExecDialogBox主要完成如下几件事:

        根据传进来的对话框资源定义数组创建对话框及对话框中的子窗体.

        根据传进来的窗口消息处理函数,记载到一全局变量保存,当这个全局变量中记载的函数指针为非空时,执行消息LOOP,消息LOOP中会将当前的MOUSE及KEY消息发送给当前焦点窗体.

        当对话框关闭时,记载对话窗体消息回调函数的全局变量会被清为0,此时消息LOOP就会退出,对话框结束.

二、发现存在的问题-----点击OK后无论先关闭消息框还是对话框,另一个不再响应.

点击对话框的OK后弹出消息框, 会出现当按下对话框的Cancel关闭对话框后, 弹出的消息框就没有任何响应的情况. 或者是关闭掉弹出的消息框, 对话框就没有任何响应的情形:从外部粗步分析的原因是调用MainTask的线程已经退出了, 这个线程是在模拟器中开启的专门用于运行GUI任务的线程,它的线程函数是Thread, Thread函数里调用main,main中再调用MainTask,所以该线程退出后也就代表UCGUI任务已经结束了。这是从模拟器的角度来分析, 现在我们分析一下为什么MainTask的调用线程会这么早退出呢?

由我们第一节中关于GUI_ExecDialogBox所做的几件中可以分析到, 当UCGUI中有一个独立的窗体退出后_cb会被清为0, 此时退出GUI窗口LOOP. 即结束了UCGUI窗口消息处理。

其实, GUI_MessageBox弹出的消息框其实也是一种对话框, 这最终调用的还是GUI_ExecDialogBox,开始我们就分析过,进入这个函数后,会有一个全局变量记录当前对话框窗体的消息处理函数指针,但是目前的问题如下:

        已经建立了两个这样的对话框窗体,这样一个全局变量来记载当前对话框的窗体消息处理函数指针显然不够,而且先前打开的对话框的的用户指定的窗体消息回调函数已经不再被调用了,此时第一个对话框的由子窗体回传到父窗体的消息均会传到第二次打开的对话框的用户指定的窗体消息回调函数中.

        第二次弹出消息框再次进入GUI_ExecDialogBox中的 while循环后,先前的对话框中的while循环就被挂起了,直至第二次的GUI_ExecDialogBox中的 while循环退出,无论关闭消息框还是对话框,都会导致知退出第二次消息LOOP。第二次消息LOOP退出后返回点为弹出消息框后的下一句,直至返回到第一个对话框的while循环后退出GUI_ExecDialogBox.

但我们期待的结果是,点击对话框的OK弹出消息框, 关闭掉对话框或是消息框,其它的都要对话框继续有反应,下面我们就来分析一下如何达到这个目标,看看要做些什么具体的改动:

三、UCGUI中的消息LOOP处理分析-----寻找问题的解决办法.

在我们发现这个问题, 我们已经粗步分析了,问题不是出在我们编写程序上, 而上UCGUI的内部,那么要解决这个问题, 我们就要进一步了解UCGUI的窗口体系。其实换一句话说,在嵌入式应用中,窗口的强大直接决定到GUI系统的体积大小,并不是所有的情况都要有这种支持,当然我们希望在下一版本可以有多个对话框的直接支持。

创建对话框:

void MainTask(void)

{

GUI_Init();

WM_SetDesktopColor(GUI_RED);

WM_SetCreateFlags(WM_CF_MEMDEV);

GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0);

}

   上面是我们创建对话框的程序,是我们编写的代码, GUI_ExecDialogBox()这个函数的作用我们已经分析过了,它所做的事用一句话来说就是创建对话框并进入窗体消息LOOP处理,下面将详细分析一下LOOP消息的处理流程:

int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,

                     int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,

                     int x0, int y0)

{

_cb = cb;

GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog,hParent, x0, y0);

while(_cb){

    if (!GUI_Exec())

      GUI_X_ExecIdle();

}

return _r;

}

   这个LOOP类似我们非常熟悉的WIN下面的消息LOOP, 其原理是一致的. GUI_CreateDialogBox负责创建对话框的所有子窗体,特别注意它其中一个参数传入是Dialog.c中定义的_cbDialog,这个函数什么也没做,基本上是转而调用_cb,后面我们会提到关于它的修改。_cb是对话框的用户定义窗口消息处理函数,这里面有一个判断,就是_cb非空时,才进行消息LOOP, _cb在Dialog.c中的定义为:[static WM_CALLBACK* _cb;] _cb是一个全局变量,我们程序中创建对话框与弹出消息框时两次调用了GUI_ExecDialogBox,后一次的_cb将会把前面的值冲,它是用户自定义的窗口消息处理函数。

   在while中有判断, 那么可见_cb是在GUI_Exec之中是有使用的,对话框的FrameWin子窗体消息流转调如下面的所示,窗口消息处理函数是在WM_SendMessage中通过函数指针的调用中, 注意[]内部的就是真正被调用来处理消息的函数:

GUI_Exec-->GUI_Exec1-->WM_Exec-->WM_Exec1-->WM_HandlePID-->WM_SendMessage-->(*pWin->cb)(pMsg)[_FRAMEWIN_Callback]-->_OnTouch()-->(*cb)(pMsg)[_cbDialog]--> *_cb)(pMsg)[_MESSAGEBOX_cbCallback]

        WM_HandlePID()------专门处理类似MOUSE的滑动操作外设消息的函数.

        WM_SendMessage()----基层的发送消息的函数,即调用相对应的窗体的消息回调函数来处理消息.

现在讲到了窗体消息LOOP,在窗体系统中最根本一点的就是对外部输入消息的处理,窗体就是靠消息驱动的,其处理代码如下:

int WM_Exec1(void){

if(WM_pfPollPID){/* Poll PID if necessary */

    WM_pfPollPID();

}

if(WM_pfHandlePID){

    if (WM_pfHandlePID())

      return 1;             /* We have done something ... */

}

if(GUI_PollKeyMsg()){

    return 1;               /* We have done something ... */

}

if(WM_IsActive && WM__NumInvalidWindows) {

    WM_LOCK();

    _DrawNext();

    WM_UNLOCK();

    return 1;               /* We have done something ... */

}

return 0;                  /* There was nothing to do ... */

}

它主要完成如下几件事:

        Poll PID中Poll个词准确的意思应该是统计/测试的意思,这里是调用用户的统计测试滑动操作外设的一个接口,用户可以通过WM_SetpfPollPID()函数来设置自己用于统计/测试滑动操作外设的具体函数。

        处理滑动操作外设 WM_TOUCH消息,真正的处理是在函数WM_HandlePID()中处理的,在后面滑动外设消息处理流程时有详细说明,在新版中更细分此消息为WM_PID_STATE_CHANGED/ WM_MOUSEOVER/ WM_TOUCH三种消息,其实在WIN下面类似消息的处理更为复杂,有移动/滚动/单击DOWN及UP[左右键]/双击[左右键]等七八种MOUSE消息,而且这些消息又分为窗体体客户区与标题区的差别,标题区的都会在消息上加上NC的前辍,如WM_NCLBUTTONUP标题区单击弹起消息。从这里我们也可以看到UCGUI中非常简化的处理,简单得不能再简单了,的确是一个微型的GUI图形支持系统。

        按键式外设消息处理,GUI_PollKeyMsg()函数在发现有新的按键消息生时会调用WM_OnKey()将消息发送到当前焦点窗体处理,如果一直处于按键按下状态时则会将前按钮的虚拟码存在一全部变量中,以供GUI_GetKey()调用来返回当前按下键值。UCGUI中有一个外部的键盘接口,外界通过GUI_StoreKeyMsg()发送键盘消息给UCGUI以驱动键盘,在我的模拟器当中就是将LCD模拟显示屏窗口的所有键盘消息通过GUI_StoreKeyMsg()传送到UCGUI中以驱动键盘消息处理,关于键盘消息的处理UCGUI中也是来一个处理一个,没有任何缓冲处理,如果某些按钮消息处理用时过长,就会造成其后的一些按键消息丢失。

static int _Key;         //记载当前按键,GUI_GetKey时返回此值  

static int _KeyMsgCnt;   //当前键盘消息数量

static struct {

int Key;               //键盘虚拟码…

int PressedCnt;        //按键次数…

} _KeyMsg;

上面是键盘消息结构,UCGUI中以一个全局的_KeyMsg键盘变量记载当前最新键盘消息,当前按键值用_Key,每产生按下键时用GUI_StoreKey更新一次此值,UCGUI中没有按键弹起消息的处理。

        检测是否有无效窗体,如果有无效窗体,则向该无效窗体发送重画消息,有一个全局变量WM__NumInvalidWindows用于记载当前无效窗体的数目,在函数_DrawNext()中每次重画一个无效窗体,查找无效窗体时是通过遍历查找的方法,先前说过窗体基本结构中有一个成员hNextLin记载下一个窗体,就在是此处用于遍历所有窗体,找出无效的窗体,发送WM_PAINT消息给窗体。注意这里每次画一个窗体的原因就是为了不影响窗体的消息处理,如果在此处用时太多,会严重影响消息处理的反应速度。

了解了UCGUI中消息处理的具体流程,那么再来分析这个先前提到的问题:无论是消息框还是对话框哪一个先被关掉, 都会掉用GUI_EndDialog,将_cb被清为零,也就意味着消息LOOP到此结束了,所以后面另外一个未被关掉的当然不会再有任何响应了!

void GUI_EndDialog(WM_HWIN hWin, int r) {

_cb = NULL;

_r = r;                //通知WM_Exec等消息LOOP返回…

WM_DeleteWindow(hWin);//free该窗体结构占用的内存…

}

现在我们可以得出一个结论:UCGUI中对话框的设计只支持单窗口的消息处理,如果要多窗口的支持,可以如同示例中一样,启用多任务支持,不然在单任务下一个MainTask中只能支持一个独立窗体,但是如果我们只是为了要弹出一个消息框而启动一个任务, 这未免太不实际。

了解UCGUI后初步修改路分析如下:

        消息传送-----经过详细的分析,认识到在消息处理中创建一个对话框窗体后,必须建立一个消息LOOP处理,来向UCGUI中的窗口捕捉并传送外设的输入消息,消息的处理实质上是通过WM_SendMessage函数来调用相应的窗口的消息回调函数。

        消息LOOP----如果创建多个对话框窗体,则会进入一个新的消息LOOP处理层而挂起原来的消息LOOP处理,要避免这种情况发生必须将消息LOOP移到MainTask之外,并在创建完所有对话框之后执行消息LOOP处理。

        消息分发-----用一个数组将所有创建对话框的自定义消息回调函数存放起来,然后在对话框消息分布处(_cbDialog函数处)对应分发各个对话框的消息,要注意和解决的问题是,必须根据消息所对应用窗体来正确分布。

        删除窗体-----在清除独立窗体时,必须将此对话框对应的用户自定义的窗体消息回调函数清零,并清除该窗体与其它窗体的数据关系及其占用资源,使其退出消息处理。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多