Window Server 中的 Events (事件)被应用程序获取并处理。类似于微软 Windows 操作系统中的消息,事件通常源于用户的输入,由 Wserv 生成发送给客户端程序,如请求窗体重画。客户端程序通过 WServ 会话来发送请求并获取事件。大部分事件(如键盘和鼠标事件)都封装为 TWsEvent 类。重画事件告诉应用程序屏幕的哪个区域需要重画,这类事件被封装在 TWsRedrawEvent 类中。
事件类型
Symbian OS 中的 Window server 有三种事件类型,通用事件、重画事件及优先键事件,它们分别封装在 TWsEvent 、 TWsRedrawEvent 、 TWsPriorityKeyEvent 类中。如下图所示:
图 ( 1 )
这三种事件分别通过 RWsSession 的 GetEvent() 、 GetRedraw() 、 GetPriorityKey() 来获取。其中通用事件又可以通过 TWsEvent 的 Type() 方法来获取事件码以区分不同的事件,如 KeyUp 、 KeyDown 事件等;通过 TWsEvent 的 Pointer() 方法来获取指针事件( TPointerEvent ), TWsEvent 的 Key() 方法来获取按键事件( TKeyEvent )。
需要指出的是,这里的 Pointer (指针)是泛指指向屏幕的设备,如鼠标,输入笔,手指等。
下面对着三种类型的事件分别作一简单的介绍。
通用事件
通用事件对应的类是 TWsEvent ,代表了用户输入事件如指针和按键事件,这类事件用活动对象以标准优先级来处理。除重画和优先键事件外的通用事件由 Window Server 生成 TWsEvent 并发送给客户端,客户端通过异步方法 RWsSession::EventReady() 来请求事件,此执行方法结束后,会触发 CActive::RunL() ,在 RunL 函数里,使用 RWsSession::GetEvent() 来获取 TWsEvent 所代表的事件。然后通过 TWsEvent::Type() 和 TWsEvent::EventData() 来识别事件代码并获取相应的事件数据,从而进行处理。先表给出了不同的事件代码对应的事件数据和窗体句柄:
|
|
|
EEventNull |
n/a |
n/a |
EEventKey |
TKeyEvent |
RWindowGroup |
EEventKeyUp |
TKeyEvent |
RWindowGroup |
EEventKeyDown |
TKeyEvent |
RWindowGroup |
EEventModifiersChanged |
TModifiersChangedEvent |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableModifierChangedEvents() was called |
EEventPointer |
TPointerEvent |
RWindow client handle |
EEventPointerEnter |
TPointerEvent |
RWindow client handle |
EEventPointerExit |
TPointerEvent |
RWindow client handle |
EEventPointerBufferReady |
None-call RWindowBase::RetrievePointerMoveBuffer() RWindow client handle |
RWindow client handle |
EEventDragDrop |
TPointerEvent |
RWindow client handle |
EEventFocusLost |
None |
RWindowGroup client handle |
EEventFocusGained |
None |
RWindowGroup client handle |
EEventSwitchOn |
None |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableOnEvents() was called |
EEventPassword |
None |
The client handle of the RWindowGroup of the window on which RWindowBase::PasswordWindow() was called |
EEventWindowGroupsChanged |
None |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableGroupChangeEvents() was called |
EEventErrorMessage |
TWsErrorMessage |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableErrorMessages() was called |
EEventMessageReady |
None |
RWindowGroup client handle |
EEventMarkInvalid |
n/a |
n/a |
EEventSwitchOff |
None |
The client handle of the RWindowTreeNode passed into RWsSession::RequestOffEvents() |
EEventKeySwitchOff |
None |
The client handle of the RWindowTreeNode passed into RWsSession::RequestOffEvents() |
EEventScreenDeviceChanged |
None |
The client handle of the object on which RWindowGroup::EnableScreenChangeEvents() was called |
EEventFocusGroupChanged |
None |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableFocusChangeEvents() was called |
EEventCaseOpened |
None |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableOnEvents() was called |
EEventCaseClosed |
None |
The client handle of the RWindowTreeNode passed into RWsSession::RequestOffEvents() |
EEventWindowGroupListChanged |
None |
The client handle of the RWindowTreeNode on which RWindowTreeNode::EnableGroupListChangeEvents() was called |
EEventKeyRepeat |
n/a (only sent to the key click plug-in interface) |
n/a |
EEventUser |
n/a |
n/a |
表( 1 )
重画事件
重画事件对应的类是 TWsRedrawEvent ,这类事件用活动对象以较低优先级来处理。 TWsRedrawEvent 通过 RWsSession::RedrawReady() 来请求,请求返回后,重画事件可以通过 RWsSession::GetRedraw() 来获得事件。重画事件数据中包括了要重画的窗体的句柄及重画区域。
优先键事件
优先键事件对应的类是 TWsPriorityKeyEvent ,这类事件 用活动对象以较高优先级来处理。在使用优先键事件机制前需要先用 RWindowGroup::AddPriorityKey() 来配置优先键。优先键通常是为应用程序提供“放弃( Abort )”或“退出( Escape )”键,即应用程序遇到这种事件要优先响应。
所有的优先键事件由 Window Server 生成并以 TWsPriorityKeyEvent 发送给客户端,客户端通过 RWsSession::GetPriorityKey() 来获取事件。
事件的处理( Handling Events )
所有的 Window Server 应用程序都处理标准事件,也几乎都处理重画事件,然而,却很少处理优先键事件。很少有程序不处理重画事件,除非程序所有窗体都是 backed-up 窗体。事件总是伴随着窗体的,如按键事件伴随 Window group ;指针事件伴随可画窗体( drawable window )。这些事件都包含一个句柄,用来识别窗体,这个句柄就是窗体在创建时的句柄。在处理事件时, Symbian 推荐不同的时间在不同的活动对象( Active Object )中处理,这样可以保证事件在不同优先级下分别被处理。
Window Server 在服务端为应用程序维护一个事件队列缓冲区,每个应用程序有自己的事件队列区(这仅仅是对于通用事件来说,重画和优先键事件是另一种不同的处理机制)。应用程序必须适当地处理好事件。应用程序在处理时,首先通过 RWsSeeeion::GetEvent() 来获取事件,然后在分析处理事件。事件必须被快速处理以相应用户的输入,如果事件的处理比较复杂费时,应该将处理分成多个小的处理块,没个处理块在不同的活动对象中,且运行优先级低于 GetEvent() 。 下面分类介绍事件的具体处理方法。
在处理事件时我们要先定义一个活动对象类,从 CActive 派生,自己实现 RunL() 。下面的代码段声明了一个处理事件的活动对象类 CClientEventExample :
/* 活动对象类中包含一个 Window Server session. */
class CClientEventExample : public CActive
{
public :
// 继承自 CActive
void RunL ();
void DoCancel ();
// 向 Window Server 请求事件
void IssueRequest();
private :
// Access to Window Server session
RWsSession & iWs ;
// Access to screen device
CWsScreenDevice & iScreen ;
// 用来保存收到的事件
TWsEvent iWsEvent ;
};
一旦活动对象被创建,就可以发送请求。我们可以调用 IssueRequest() 来发送一个异步请求,这个请求前面提到过,使用 RWsSession 的 EventReady() 方法,其中参数 TRequestStatus 是活动对象的成员变量 iStatus ,如下代码段所示:
/* 向 Window Server 请求事件 */
void CClientEventExample ::IssueRequest ()
{
// 请求事件
iWs.EventReady(&iStatus);
// 设置活动对象为 active ,即当这个异步请求返回时, Active scheduler 会调用 RunL
SetActive ();
}
当客户端程序事件请求返回后,活动对象的 RunL() 函数会被调用。前面提到过,在 RunL() 获取事件对象,并根据不同事件代码和事件数据进行不同的处理。如下代码所示:
/* 当有事件发生时, Active scheduler 调用 RunL */
void CClientEventExample ::RunL ()
{
// 用 Window Server session iWs 来获取事件
iWs . GetEvent ( iWsEvent );
// 获取事件代码,事件代码在 TEventCode 中定义
TInt eventType= iWsEvent . Type ();
// 处理事件
switch (eventType)
{
/* 根据不同的事件代码来处理不同的事件 */
/* 如按键事件 */
case EEventKey:
case EEventKeyUp:
case EEventKeyDown:
{
// 由表( 1 )可知,这几个事件对应的事件数据类型是 TKeyEvent
// 获取 key event
TKeyEvent& keyEvent=*iWsEvent.Key();
// 从 TKeyEvent 中获得是哪个按键
TUint code = keyEvent.iCode;
break ;
}
/* 如控制或换档键事件 (e.g. SHIFT) */
case EEventModifiersChanged:
{
// 由表( 1 )可知,这个事件对应的事件数据类型是 TModifiersChangedEvent
// 获取 modifier keys
TModifiersChangedEvent & keyEvent=*iWsEvent.ModifiersChanged();
// 获取具体的换挡或控制键(在 TEventModifier 中有定义)
TUint modifiers = keyEvent.iModifiers;
break ;
}
// 处理用户自定义的事件
case EEventUser:
{
// 获取事件数据
TAny* data = iWsEvent.EventData();
break ;
}
...
}
}
无论是窗体、窗体组还是系统事件,都在如上所示的 RunL 中处理。具体如何获取事件数据和窗体句柄,查表( 1 )即可。
另外需要说明的是,还有类事件即 TRawEvent ,它是最原始的系统事件,来自硬件设备如键盘,鼠标或手写笔等。这类事件从硬件产生,送到系统内核的事件队列中,由内核处理后再生成前面讲的应用层的事件。