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,它是最原始的系统事件,来自硬件设备如键盘,鼠标或手写笔等。这类事件从硬件产生,送到系统内核的事件队列中,由内核处理后再生成前面讲的应用层的事件。