windowing/windowserver/nga/SERVER/EVENT.CPP
changeset 116 171fae344dd4
parent 0 5d03bc08d59c
--- a/windowing/windowserver/nga/SERVER/EVENT.CPP	Tue Jun 22 15:21:29 2010 +0300
+++ b/windowing/windowserver/nga/SERVER/EVENT.CPP	Fri Jul 16 11:45:55 2010 +0300
@@ -1,4 +1,4 @@
-// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
+// Copyright (c) 1994-2010 Nokia Corporation and/or its subsidiary(-ies).
 // All rights reserved.
 // This component and the accompanying materials are made available
 // under the terms of "Eclipse Public License v1.0"
@@ -18,6 +18,7 @@
 #include "EVENT.H"
 
 #include "W32STD.H"
+#include <e32uid.h>
 #include <hal.h>
 #include <w32adll.h>
 #include "W32CLICK.H"
@@ -32,12 +33,16 @@
 #include "pointer.h"
 #include "debugbar.h"
 #include "advancedpointereventhelper.h"
+#include "Graphics/wsgraphicdrawerinternal.h"
 
 GLREF_D CDebugLogBase *wsDebugLog;
 
 GLREF_C void StateDump();
 GLREF_C void HeapDump();
 
+_LIT(KDefaultKeyRouterPluginName, "keyrouter.dll");
+_LIT(KWSERVIniFileVarKeyRouterPlugin, "KEYROUTERPLUGIN");
+
 #define IMPOSSIBLE 0xFFFFFFFF
 
 const TWsWinCmdCaptureKey ImpossibleKeyPress=
@@ -160,6 +165,8 @@
 TEventRequestQueue TWindowServerEvent::iScreenDeviceChangedQueue;
 TTime TWindowServerEvent::iPrevOomMessageTime;
 CCaptureKeys *TWindowServerEvent::iCaptureKeys;
+CKeyEventRouter* TWindowServerEvent::iKeyEventRouter;
+RLibrary TWindowServerEvent::iKeyEventRouterLibrary;
 CWsHotKey *TWindowServerEvent::iHotKeys;
 TInt TWindowServerEvent::iModifierState;
 CRawEventReceiver *TWindowServerEvent::iEventReceiver;
@@ -172,12 +179,12 @@
 TInt TWindowServerEvent::iEventHandlerCount=0;
 TRepeatKey CKeyboardRepeat::iCurrentRepeat;
 TRepeatKey CKeyboardRepeat::iAlternateRepeat;
+TRepeatKey CKeyboardRepeat::iLongRepeat;
 TInt CKeyboardRepeat::iRepeatRollover=1;
 CKeyboardRepeat::TRepeatType CKeyboardRepeat::iRepeating=ERepeatNone;
 CKeyboardRepeat *CKeyboardRepeat::iThis=NULL;
 TTimeIntervalMicroSeconds32 CKeyboardRepeat::iInitialTime;
 TTimeIntervalMicroSeconds32 CKeyboardRepeat::iTime;
-CWsWindowGroup *CKeyboardRepeat::iFocus=NULL;
 TBool CKeyboardRepeat::iAlternateRepeatExists=EFalse;
 CWsCaptureLongKey* CKeyboardRepeat::iLongCapture=NULL;
 
@@ -198,6 +205,8 @@
 	{
 	DeleteHotKeys();
 	delete iCaptureKeys;
+	delete iKeyEventRouter;
+	iKeyEventRouterLibrary.Close();
 	CKeyboardRepeat::Destroy();
 	delete iKeyTranslator;
 	delete iEventReceiver;
@@ -231,8 +240,57 @@
 		iKeyTranslator->ChangeKeyData(keyDataDllName);
 		}
 
+	// CCaptureKeys is no longer used but a dummy object is required for
+	// calls to CKeyTranslator::TranslateKey() until capture functionality
+	// has been removed from ektran.dll.
 	iCaptureKeys=new(ELeave) CCaptureKeys;
 	iCaptureKeys->Construct();
+
+	// Load the key event routing plug-in. The DLL name may be overridden
+	// by setting the keyword KEYROUTERPLUGIN in wsini.ini.
+	TPtrC pluginName(KDefaultKeyRouterPluginName);
+	WsIniFile->FindVar(KWSERVIniFileVarKeyRouterPlugin, pluginName);
+	const TUidType uidType(KDynamicLibraryUid, KKeyRouterPluginUid);
+	TInt err = iKeyEventRouterLibrary.Load(pluginName, uidType);
+
+	if (wsDebugLog)
+		{
+		TLogMessageText buf;
+
+		if (err == KErrNone)
+			{
+			_LIT(KLogLoadOk, "Loaded plugin '%S' UID3 0x%x");
+			const TFileName& pluginPathname = iKeyEventRouterLibrary.FileName();
+			const TUid uid3 = iKeyEventRouterLibrary.Type()[2];
+			buf.Format(KLogLoadOk, &pluginPathname, uid3.iUid);
+			}
+		else
+			{
+			_LIT(KLogLoadError, "Failed to load plugin '%S' (error %d)");
+			buf.Format(KLogLoadError, &pluginName, err);
+			}
+
+		wsDebugLog->MiscMessage(CDebugLogBase::ELogImportant, buf);
+		}
+
+	if (err != KErrNone)
+		{
+#ifdef _DEBUG
+		_LIT(KLoadError, "WServ: failed to load plugin '%S' (error %d)");
+		RDebug::Print(KLoadError, &pluginName, err);
+#endif
+		User::Leave(err);
+		}
+
+	// Create the key event router
+	typedef CKeyEventRouter* (*TCreateFunc)();
+	TCreateFunc newL = reinterpret_cast<TCreateFunc>(iKeyEventRouterLibrary.Lookup(1));
+	if (newL == NULL)
+		{
+		User::Leave(KErrNotFound);
+		}
+	iKeyEventRouter = (*newL)();
+
 	for (TInt index=0;index<TWindowServerEvent::ENumHotKeys;index++)
 		ConstructDefaultHotKeyL(index,DefaultHotKeys[index]);
 	CKeyboardRepeat::NewL();
@@ -312,6 +370,7 @@
 	captureKey.modifiers=aHotKey.modifiers;
 	captureKey.modifierMask=aHotKey.modifierMask;
 	captureKey.key=aHotKey.keycode;
+	captureKey.priority = 0;
 	hotKey->ConstructLD(captureKey);
 //
 	LinkHotKey(hotKey);
@@ -622,6 +681,21 @@
 	TWsEvent event;
 	event.SetType(EEventDisplayChanged);
 	event.SetTimeNow();
+	
+    // fill in the handle otherwise CONE will discard the notification
+    CWsObjectIx* clientObjList = aWsClient->ObjectIndex();
+    const TWsObject* ptr=clientObjList->FirstObject();
+    const TWsObject* end=ptr+clientObjList->Length();
+    while(++ptr<end)    // first one should always have a NULL object
+        {
+        const CWsObject* obj=ptr->iObject;
+        if (obj && obj->Type()==WS_HANDLE_GROUP_WINDOW)
+            {
+            event.SetHandle(ptr->iHandle);
+            break;
+            }
+        }	
+	
 	TWsDisplayChangedEvent* dispEvent = event.DisplayChanged();
 	dispEvent->iDisplayNumber = aDisplayNumber;
 	dispEvent->iConfigurationChangeId = aConfigurationChangeId;
@@ -641,23 +715,68 @@
 	aWin->EventQueue()->QueueEvent(aEvent, aPriority);
 	}
 
-void TWindowServerEvent::QueueKeyPress(const TKeyData& aKey, TInt aScanCode, CWsWindowGroup* aRepeatFocus, TBool aCheckRepeat,TInt aRepeats)
+/**
+Process a key press event.
+
+This function is called for every input key event and uses the Key Event
+Routing plug-in to check for short and long key capture and determine the
+destination window group for the queued event(s).
+Window server hotkeys are also processed.
+Note that the key repeat timer is started here but the key repeat events
+generated by the timer go directly to QueueKeyPress().
+
+@param	aKeyEvent		Input key event
+@param	aCheckRepeat	Check for key repeat and long key capture
+@param	aRepeats		Repeat count
+*/
+void TWindowServerEvent::ProcessKeyPress(const TKeyEvent& aKeyEvent, TBool aCheckRepeat, TInt aRepeats)
  	{
-	CWsWindowGroup* focusWin=CWsTop::FocusWindowGroup();
-	TWsEvent event;
-	TKeyEvent& keyEvent=*event.Key();
-	keyEvent.iCode=aKey.iKeyCode;
-	keyEvent.iScanCode=aScanCode;
-	keyEvent.iModifiers=aKey.iModifiers;
-	keyEvent.iRepeats=aRepeats;
-	if (!aRepeatFocus && CClick::IsHandler())
-		CClick::KeyEvent(EEventKey,keyEvent);
-	CWsCaptureLongKey* longCapture=NULL;
-	if (aCheckRepeat)
-		longCapture=CWsCaptureLongKey::CheckForCapture(aKey.iKeyCode, aKey.iModifiers);
-	if (aKey.iIsCaptureKey)
+	CWsWindowGroup* focusWin = CWsTop::FocusWindowGroup();
+	TUid focusAppUid = focusWin ? TUid::Uid(focusWin->Client()->SecureId().iId) : KNullUid;
+
+	// Route the key event and check for short key capture.
+	// Note that the Key Routing plugin may translate or block key events.
+	TKeyEventRouterInput input(ECaptureTypeKey, aKeyEvent, focusWin, focusAppUid);
+	TKeyEventRouterOutput output;
+
+#ifdef _DEBUG
+	// RouteKey() must not fail. Check for leaves in case the plug-in
+	// is badly behaved.
+	TRAPD(err, iKeyEventRouter->RouteKey(input, output));
+	WS_ASSERT_DEBUG(err == KErrNone, EWsPanicKeyEventRouterLeave);
+#else
+	iKeyEventRouter->RouteKey(input, output);
+#endif
+
+	WS_ASSERT_DEBUG(output.iResult == ERouted || output.iResult == ECaptured || output.iResult == EConsumed, EWsPanicKeyEventRouterBadResult);
+
+	if (output.iResult == EConsumed)
 		{
-		if (aKey.iApp==NULL)	// Captured by Wserv itself
+		focusWin = NULL;
+		}
+	else
+		{
+		focusWin = static_cast<CWsWindowGroup*>(output.iWindowGroup);
+		}
+	WS_ASSERT_DEBUG((focusWin == NULL || focusWin->Type() == WS_HANDLE_GROUP_WINDOW) && (output.iResult != ERouted || focusWin == CWsTop::FocusWindowGroup()), EWsPanicKeyEventRouterBadWindowGroup);
+
+	// Ensure that short event is not marked with EModifierLongKey
+	output.iKeyEvent.iModifiers &= ~EModifierLongKey;
+
+	// Generate key click unless the event is consumed. This is consistent
+	// with the behaviour when CKeyTranslator::TranslateKey() yields no
+	// translation for a particular scan code. (Click events for key up/down
+	// will still be generated by QueueKeyUpDown()). Note however that a long
+	// key press may still be captured even if the short event is consumed.
+	if (CClick::IsHandler() && output.iResult != EConsumed)
+		{
+		output.iKeyEvent.iRepeats = aRepeats;
+		CClick::KeyEvent(EEventKey, output.iKeyEvent);
+		}
+
+	if (output.iResult == ECaptured)
+		{
+		if (output.iWindowGroup == NULL)	// Captured by Wserv itself
 			{
 			_LIT(KWSERVDebugLogCapturedKey,"WSERV Captured Key");
 			CScreen* focusScreen=CWsTop::CurrentFocusScreen();
@@ -668,7 +787,7 @@
 			CWsHotKey *hotKey=iHotKeys;
 			while(hotKey)
 				{
-				if (hotKey->KeyHandle()==aKey.iHandle)
+				if (hotKey->KeyHandle() == reinterpret_cast<TInt>(output.iCaptureHandle))
 					{
 					switch(hotKey->HotKeyType())
 						{
@@ -753,22 +872,85 @@
 			WS_PANIC_ALWAYS(EWsPanicUnknownCaptureKey);
 			return;
 			}
-		focusWin=((CWsWindowGroup *)aKey.iApp);
+
 		_LIT(KWSERVDebugLogKeyCapturedByApp,"Key captured by app %d");
 		if (wsDebugLog)
 			wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything,KWSERVDebugLogKeyCapturedByApp,focusWin->Identifier());
 		if (CWsPassword::PasswordModeActive() && focusWin!=CWsPassword::PasswordWindow()->WinGroup())
 			return;
 		}
-	if (aRepeatFocus && aRepeatFocus!=focusWin)
-		CKeyboardRepeat::CancelRepeat(NULL);		// Repeat is going to different window so cancel it and don't deliver this key
-	else if (focusWin!=NULL && focusWin->CheckForPriorityKey(aKey,aScanCode)==EFalse)
+
+	CWsCaptureLongKey* longCapture = NULL;
+	TKeyEventRouterOutput longOutput;
+	if (aCheckRepeat)
 		{
-		if (longCapture || (aCheckRepeat && !aRepeatFocus && aKey.iModifiers&EModifierAutorepeatable))
+		// Check for long key capture.
+		// Note that a long key event can only result from capture, there is
+		// no default detection or routing of long events.
+		input.iType = ECaptureTypeLongKey;
+#ifdef _DEBUG
+		TRAPD(err, iKeyEventRouter->RouteKey(input, longOutput));
+		WS_ASSERT_DEBUG(err == KErrNone, EWsPanicKeyEventRouterLeave);
+#else
+		iKeyEventRouter->RouteKey(input, longOutput);
+#endif
+
+		if (longOutput.iResult == ECaptured)
+			{
+			longCapture = static_cast<CWsCaptureLongKey*>(longOutput.iCaptureHandle);
+
+			// Mark long key events with EModifierLongKey so that applications
+			// can easily distinguish short and long events.
+			longOutput.iKeyEvent.iModifiers |= EModifierLongKey;
+
+			// Start timer to detect long key press
+			CKeyboardRepeat::StartRepeat(aKeyEvent.iScanCode, output, &longOutput);
+			}
+		else if (output.iResult != EConsumed && output.iKeyEvent.iModifiers & EModifierAutorepeatable)
 			{
-			if (CKeyboardRepeat::StartRepeat(aKey,aScanCode,focusWin,longCapture))
-				return;
+			// Start timer for key repeat
+			CKeyboardRepeat::StartRepeat(aKeyEvent.iScanCode, output, NULL);
 			}
+		}
+
+	// Queue the short event
+	if (!longCapture || longCapture->iFlags & ELongCaptureShortEventImmediately)
+		{
+		QueueKeyPress(output, EFalse, aRepeats);
+		}
+	}
+
+/**
+Queue a key press event.
+
+This function is called for each key event produced by ProcessKeyPress(),
+for every key repeat and long key event generated by the timer and also for
+delayed short key events from KeyUp().
+
+@param	aOutput			Output key event from routing plug-in
+@param	aIsRepeat		Event is due to key repeat
+@param	aRepeats		Repeat count
+*/
+void TWindowServerEvent::QueueKeyPress(const TKeyEventRouterOutput& aOutput, TBool aIsRepeat, TInt aRepeats)
+ 	{
+	if (aOutput.iResult == EConsumed)
+		{
+		// Don't deliver this key
+		return;
+		}
+
+	TWsEvent event;
+	TKeyEvent& keyEvent = *event.Key();
+	keyEvent = aOutput.iKeyEvent;
+	keyEvent.iRepeats = aRepeats;
+
+	CWsWindowGroup* focusWin = static_cast<CWsWindowGroup*>(aOutput.iWindowGroup);
+	WS_ASSERT_DEBUG(focusWin == NULL || focusWin->Type() == WS_HANDLE_GROUP_WINDOW, EWsPanicKeyEventRouterBadWindowGroup);
+
+	if (aIsRepeat && aOutput.iResult != ECaptured && focusWin != CWsTop::FocusWindowGroup())
+		CKeyboardRepeat::CancelRepeat(NULL);		// Repeat is going to different window so cancel it and don't deliver this key
+	else if (focusWin != NULL && focusWin->CheckForPriorityKey(keyEvent) == EFalse)
+		{
 		event.SetType(EEventKey);
 		event.SetHandle(focusWin->ClientHandle());
 		if (aRepeats!=0)
@@ -776,18 +958,15 @@
 			CEventQueue* queue=focusWin->EventQueue();
 			queue->Wait();
 			const TWsEvent* prev=queue->PeekLastEvent();
-			if (prev!=NULL && prev->Type()==EEventKey && prev->Key()->iRepeats>0)
+			if (prev != NULL && prev->Type() == EEventKey && prev->Key()->iRepeats > 0 && prev->Key()->iCode == keyEvent.iCode)
 				{
-				event= *prev;
-				event.Key()->iRepeats+=aRepeats;
-				queue->UpdateLastEvent(event);
+				prev->Key()->iRepeats += aRepeats;
 				queue->Signal();
 				if (CClick::IsHandler())
-					CClick::KeyEvent(EEventKeyRepeat,*event.Key());
+					CClick::KeyEvent(EEventKeyRepeat, *prev->Key());
 				return;
 				}
 			queue->Signal();
-			event.Key()->iRepeats=aRepeats;
 			if (CClick::IsHandler())
 				CClick::KeyEvent(EEventKeyRepeat,keyEvent);
 			}
@@ -795,24 +974,69 @@
 		}
 	}
 
+/**
+Queue a key up/down event.
+
+@param	aRawEvent		Raw event
+*/
 void TWindowServerEvent::QueueKeyUpDown(const TRawEvent &aRawEvent)
  	{
-	CWsWindowGroup *focusWin=CWsCaptureKeyUpsAndDowns::CheckForCapture(aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE, iModifierState);
-	if (!focusWin)	// If not captured
-		focusWin=CWsTop::FocusWindowGroup();
-	TWsEvent event;
-	TEventCode type=aRawEvent.Type()==TRawEvent::EKeyUp ? EEventKeyUp : EEventKeyDown;
-	event.Key()->iCode=0;
+	TEventCode type = aRawEvent.Type() == TRawEvent::EKeyUp ? EEventKeyUp : EEventKeyDown;
+
+	// Check for key up/down capture
+	TKeyEvent keyEvent;
+	keyEvent.iScanCode = aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE;
+#if defined(__WINS__)
+	keyEvent.iCode = __WINS_CHARCODE(aRawEvent.ScanCode());
+#else
+	keyEvent.iCode = 0;
+#endif
+	keyEvent.iModifiers = iModifierState;
+	keyEvent.iRepeats = 0;
+
+	CWsWindowGroup* focusWin = CWsTop::FocusWindowGroup();
+	TUid focusAppUid = focusWin ? TUid::Uid(focusWin->Client()->SecureId().iId) : KNullUid;
+
+	TKeyEventRouterInput input(ECaptureTypeKeyUpDown, keyEvent, focusWin, focusAppUid);
+	TKeyEventRouterOutput output;
+#ifdef _DEBUG
+	TRAPD(err, iKeyEventRouter->RouteKey(input, output));
+	WS_ASSERT_DEBUG(err == KErrNone, EWsPanicKeyEventRouterLeave);
+#else
+	iKeyEventRouter->RouteKey(input, output);
+#endif
+
+	if (output.iResult == EConsumed)
+		{
+		// Don't deliver this key. A key click is still generated for the
+		// input event.
+		if (CClick::IsHandler())
+			{
+			CClick::KeyEvent(type, keyEvent);
+			}
+		return;
+		}
+	WS_ASSERT_DEBUG(output.iResult == ERouted || output.iResult == ECaptured, EWsPanicKeyEventRouterBadResult);
+
+	focusWin = static_cast<CWsWindowGroup*>(output.iWindowGroup);
+	WS_ASSERT_DEBUG((focusWin == NULL || focusWin->Type() == WS_HANDLE_GROUP_WINDOW) && (output.iResult != ERouted || focusWin == CWsTop::FocusWindowGroup()), EWsPanicKeyEventRouterBadWindowGroup);
 #if defined(__WINS__)
 	if (focusWin && !focusWin->WsOwner()->RemoveKeyCode())
-		event.Key()->iScanCode=aRawEvent.ScanCode();
-	else
+		{
+		// Restore WINS character code
+		output.iKeyEvent.iScanCode |= output.iKeyEvent.iCode;
+		}
+	output.iKeyEvent.iCode = 0;
 #endif
-	event.Key()->iScanCode=aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE;
-	event.Key()->iModifiers=iModifierState;
-	event.Key()->iRepeats=0;
+
+	output.iKeyEvent.iRepeats = 0;
 	if (CClick::IsHandler())
-		CClick::KeyEvent(type,*event.Key());
+		{
+		CClick::KeyEvent(type, output.iKeyEvent);
+		}
+
+	TWsEvent event;
+	*event.Key() = output.iKeyEvent;
 	if (focusWin!=NULL)
 		{
 		event.SetType(type);
@@ -921,6 +1145,11 @@
 		}
 	}
 
+/*
+Process a raw event
+
+@param	aRawEvent	Raw event
+*/
 void TWindowServerEvent::ProcessRawEvent(const TRawEvent& aRawEvent)
 //
 // Event has completed.
@@ -1020,23 +1249,35 @@
 		case TRawEvent::EKeyDown:
 			{
 			_LIT(KWSERVDebugLogKeyDownArrival,"Key down arrives %d");
-			if(CDebugBar* dbg = CWsTop::Screen()->DebugBar())
+			CScreen* screen = CWsTop::Screen();
+			WS_ASSERT_ALWAYS(screen, EWsPanicNoScreen);
+			if(CDebugBar* dbg = screen->DebugBar())
 				dbg->OnKeyEvent();
 			if (wsDebugLog)
 				wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything,KWSERVDebugLogKeyDownArrival,aRawEvent.ScanCode());
 			CKeyboardRepeat::KeyDown();
 			TKeyData keyData;
+			// Note iCaptureKeys is needed as dummy arg only. Key capture is
+			// now handled in ProcessKeyPress().
 			TBool translated=iKeyTranslator->TranslateKey(aRawEvent.ScanCode(), EFalse,*iCaptureKeys,keyData);
 			ProcessModifierChanges();
 			QueueKeyUpDown(aRawEvent);
 			if (translated)
-				QueueKeyPress(keyData,aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE,NULL,ETrue,0);
+				{
+				TKeyEvent keyEvent;
+				keyEvent.iScanCode = aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE;
+				keyEvent.iCode = keyData.iKeyCode;
+				keyEvent.iModifiers = keyData.iModifiers;
+				ProcessKeyPress(keyEvent, ETrue, 0);
+				}
 			}
 			break;
 		case TRawEvent::EKeyUp:
 			{
 			_LIT(KWSERVDebugLogKeyUpArrival,"Key up arrives %d");
-			if(CDebugBar* dbg = CWsTop::Screen()->DebugBar())
+			CScreen* screen = CWsTop::Screen();
+			WS_ASSERT_ALWAYS(screen, EWsPanicNoScreen);
+			if(CDebugBar* dbg = screen->DebugBar())
 				dbg->OnKeyEvent();
 			if (wsDebugLog)
 				wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything,KWSERVDebugLogKeyUpArrival,aRawEvent.ScanCode());
@@ -1048,7 +1289,11 @@
 			if (translated)
 				{
 				CKeyboardRepeat::CancelRepeat(NULL);
-				QueueKeyPress(keyData,aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE,NULL,EFalse,0);
+				TKeyEvent keyEvent;
+				keyEvent.iScanCode = aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE;
+				keyEvent.iCode = keyData.iKeyCode;
+				keyEvent.iModifiers = keyData.iModifiers;
+				ProcessKeyPress(keyEvent, EFalse, 0);
 				}
 			}
 			break;
@@ -1079,14 +1324,11 @@
  			_LIT(KWSERVDebugLogRepeatingKeyArrival,"Repeating key arrives %d");
  			if (wsDebugLog)
  				wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything,KWSERVDebugLogRepeatingKeyArrival,aRawEvent.ScanCode());
- 			TKeyData keyData;
- 			keyData.iModifiers=iKeyTranslator->GetModifierState();
-			keyData.iApp=0;
-			keyData.iHandle=0;
-			keyData.iIsCaptureKey=EFalse;
-			keyData.iKeyCode=aRawEvent.ScanCode(); 
-			iCaptureKeys->ProcessCaptureKeys(keyData);
-			QueueKeyPress(keyData, aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE,NULL,EFalse,aRawEvent.Repeats());
+			TKeyEvent keyEvent;
+			keyEvent.iScanCode = aRawEvent.ScanCode() __REMOVE_WINS_CHARCODE;
+			keyEvent.iCode = aRawEvent.ScanCode();
+ 			keyEvent.iModifiers = iKeyTranslator->GetModifierState();
+			ProcessKeyPress(keyEvent, EFalse, aRawEvent.Repeats());
  			}
  			break;
 		default:
@@ -1096,33 +1338,26 @@
 
 void TWindowServerEvent::ProcessKeyEvent(const TKeyEvent &aKeyEvent,TInt aRepeats)
 	{
-	TKeyData keyData;
-	keyData.iModifiers=aKeyEvent.iModifiers;
-	keyData.iApp=0;
-	keyData.iHandle=0;
-	keyData.iIsCaptureKey=EFalse;
-	keyData.iKeyCode=aKeyEvent.iCode;
 	if (CKeyboardRepeat::IsAreadyActive())
 		{
 		CKeyboardRepeat::CancelRepeat(NULL);
 		}
-	iCaptureKeys->ProcessCaptureKeys(keyData);
-	QueueKeyPress(keyData,aKeyEvent.iScanCode,NULL,aRepeats==0,aRepeats);
+	ProcessKeyPress(aKeyEvent, aRepeats == 0, aRepeats);
 	}
 
-void TWindowServerEvent::AddCaptureKeyL(const TCaptureKey &aCaptureKey)
+void TWindowServerEvent::AddCaptureKeyL(const TKeyCaptureRequest& aRequest)
 	{
-	iCaptureKeys->AddCaptureKeyL(aCaptureKey,aCaptureKey.iKeyCodePattern.iFiller);
+	iKeyEventRouter->AddCaptureKeyL(aRequest);
 	}
 
-void TWindowServerEvent::SetCaptureKey(TUint32 aHandle, const TCaptureKey &aCaptureKey)
+void TWindowServerEvent::UpdateCaptureKeyL(const TKeyCaptureRequest& aRequest)
 	{
-	iCaptureKeys->SetCaptureKey(aHandle, aCaptureKey,aCaptureKey.iKeyCodePattern.iFiller);
+	iKeyEventRouter->UpdateCaptureKeyL(aRequest);
 	}
 
-void TWindowServerEvent::CancelCaptureKey(TUint32 aHandle)
+void TWindowServerEvent::CancelCaptureKey(TKeyCaptureType aType, TAny* aHandle)
 	{
-	iCaptureKeys->CancelCaptureKey(aHandle);
+	iKeyEventRouter->CancelCaptureKey(aType, aHandle);
 	}
 
 TInt TWindowServerEvent::GetModifierState()
@@ -1463,21 +1698,27 @@
 	iTime=aTime;
 	}
 
+/**
+Process timer events.
+
+Called when the key repeat timer expires, this function generates the
+appropriate long key or repeated key event. If the timer was started for
+normal key repeat or if the long key event was captured with the automatic
+repeat option specified then the timer is restarted.
+*/
 void CKeyboardRepeat::RunL()
 	{
 	User::ResetInactivityTime();
-	//WS_ASSERT_DEBUG(iRepeating!=ERepeatNone, EWsPanicTemp);
+	WS_ASSERT_DEBUG(iRepeating != ERepeatNone, EWsPanicKeyRepeat);
 	TBool timer=ETrue;
 	if (iRepeating>=ERepeatLong)
 		{
 		// Defensive programming - iLongCapture should never be NULL if iRepeating >= ERepeatLong
+		WS_ASSERT_DEBUG(iLongCapture != NULL, EWsPanicKeyRepeat);
 		if (iLongCapture)
 			{
-			iCurrentRepeat.iKey.iApp=REINTERPRET_CAST(TUint32,iLongCapture->iWindowGroup);
-			iCurrentRepeat.iKey.iHandle=0;
-			iCurrentRepeat.iKey.iIsCaptureKey=ETrue;
-			iCurrentRepeat.iKey.iKeyCode=iLongCapture->iData.outputKey;
-			timer=iLongCapture->iData.flags&ELongCaptureRepeatEvents;
+			iCurrentRepeat = iLongRepeat;
+			timer = iLongCapture->iFlags & ELongCaptureRepeatEvents;
 			iRepeating=ERepeatLongRepeated;
 			}
 		else
@@ -1491,53 +1732,67 @@
 		After(iTime);
 	else
 		iRepeating=ERepeatNone;
-	TWindowServerEvent::QueueKeyPress(iCurrentRepeat.iKey,iCurrentRepeat.iScanCode,iFocus,EFalse,1);
+
+	TWindowServerEvent::QueueKeyPress(iCurrentRepeat.iOutput, ETrue, 1);
 	}
 
-TBool CKeyboardRepeat::StartRepeat(const TKeyData &aKey, TInt aScanCode, CWsWindowGroup *aRepeatFocus, CWsCaptureLongKey* aLongCapture)
+/**
+Start key repeat and long key press timer
+
+@param	aInputScanCode	Original scan code (before routing)
+@param	aShortEvent		Short key event (routing plug-in output)
+@param	aLongEvent		Pointer to long key event (routing plug-in output)
+						or NULL if none.
+
+Note: When aLongEvent != NULL, iCurrentRepeat reflects the short key event
+until the timer has expired. This is necessary to allow a delayed short key
+event to be delivered by KeyUp(). CancelRepeat() must therefore examine
+iCurrentRepeat or iLongRepeat according to the repeat type in iRepeat.
+*/
+void CKeyboardRepeat::StartRepeat(TInt aInputScanCode, const TKeyEventRouterOutput& aShortEvent, const TKeyEventRouterOutput* aLongEvent)
 	{
 	TTimeIntervalMicroSeconds32 time;
-	TBool ret=EFalse;
-	iCurrentRepeat.iScanCode=aScanCode;
-	iCurrentRepeat.iKey=aKey;
+	iCurrentRepeat.iInputScanCode = aInputScanCode;
+	iCurrentRepeat.iOutput = aShortEvent;
 
-	if (aLongCapture)
+	if (aLongEvent)
 		{
-		iLongCapture=aLongCapture;
-		iRepeating=ERepeatLong;
-		time=aLongCapture->iData.delay;
-		ret=!(aLongCapture->iData.flags&ELongCaptureShortEventImmediately);
-		//need window group from long capture key or even better delete it altogether.
-		iFocus=aLongCapture->WindowGroup();
+		iRepeating = ERepeatLong;
+		iLongRepeat.iInputScanCode = aInputScanCode;
+		iLongRepeat.iOutput = *aLongEvent;
+		iLongCapture = static_cast<CWsCaptureLongKey*>(aLongEvent->iCaptureHandle);
+		time = iLongCapture->iDelay;
 		}
 	else
 		{
-		iFocus=aRepeatFocus;
+		iLongCapture = NULL;
 		iRepeating=ERepeatNormal;
 		time=iInitialTime;
 		}
 	iThis->After(time);
-	return ret;
 	}
 
+/**
+Cancel key repeat processing
+*/
 void CKeyboardRepeat::doCancelRepeat()
 	{
 	iRepeating=ERepeatNone;
 	iThis->Cancel();
 	}
 
+/**
+Cancel any key repeat associated with the specified window group
+
+@param	aRepeatFocus	Destination window group or NULL for all
+*/
 void CKeyboardRepeat::CancelRepeat(CWsWindowGroup *aRepeatFocus)
 	{
-	if (aRepeatFocus==NULL || aRepeatFocus==iFocus)
+	if (iRepeating != ERepeatNone)
 		{
-		if (iRepeating)
-			doCancelRepeat();
-		iAlternateRepeatExists=EFalse;
-		}
-	else if (iRepeating >= ERepeatLong)
-		{
-		// Defensive programming - iLongCapture should never be NULL if iRepeating >= ERepeatLong
-		if (iLongCapture && iLongCapture->iWindowGroup == aRepeatFocus)
+		if (aRepeatFocus == NULL ||
+			(iRepeating == ERepeatNormal) && (aRepeatFocus == iCurrentRepeat.iOutput.iWindowGroup) ||
+			(iRepeating >= ERepeatLong) && (aRepeatFocus == iLongRepeat.iOutput.iWindowGroup))
 			{
 			doCancelRepeat();
 			iAlternateRepeatExists=EFalse;
@@ -1545,36 +1800,38 @@
 		}
 	}
 
-void CKeyboardRepeat::CancelRepeat(CWsWindowGroup *aRepeatFocus,TUint aScanCode,TBool aLongCaptureFlag,TUint aModifiers)
+/**
+Cancel any key repeat associated with the specified capture handle
+
+@param	aCaptureHandle		Handle to capture request
+@param	aLongCaptureFlag	ETrue for long key capture, EFalse for normal key
+*/
+void CKeyboardRepeat::CancelRepeat(const TAny* aCaptureHandle, TBool aLongCaptureFlag)
 	{
 	if (aLongCaptureFlag)
 		{
-		// long capture key is cancelled
-		if (iRepeating >= ERepeatLong && iCurrentRepeat.iScanCode==aScanCode)			
-				{
-				// Defensive programming - iLongCapture should never be NULL if iRepeating >= ERepeatLong
-				if (iLongCapture && aRepeatFocus == iLongCapture->iWindowGroup &&
-					(aModifiers & iLongCapture->iData.modifierMask) == iLongCapture->iData.modifiers)
-					{
-					doCancelRepeat();
-					iAlternateRepeatExists=EFalse;
-					}
-				}
+		// Cancel repeat for long capture key
+		if (iRepeating >= ERepeatLong && aCaptureHandle == iLongRepeat.iOutput.iCaptureHandle)
+			{
+			doCancelRepeat();
+			iAlternateRepeatExists=EFalse;
+			}
 		}
 	else
 		{
-		// normal capture key is cancelled
-		if (aRepeatFocus==iFocus)
+		// Cancel repeat for normal capture key
+		if (iRepeating == ERepeatNormal && aCaptureHandle == iCurrentRepeat.iOutput.iCaptureHandle)
 			{
-			if (iRepeating>=ERepeatNormal && iCurrentRepeat.iScanCode==aScanCode)
-				{
-				doCancelRepeat();
-				}
+			doCancelRepeat();
 			iAlternateRepeatExists=EFalse;
 			}
 		}
 	}
 	
+/**
+Process a key down event during key repeat.
+The current repeat data is saved for possible restoration after rollover.
+*/
 void CKeyboardRepeat::KeyDown()
 	{
 	if (iRepeating!=ERepeatNone)
@@ -1588,18 +1845,26 @@
 		}
 	}
 
+/**
+Process a key up event during key repeat.
+Send delayed short key event if necessary for long key event processing.
+Switch to alternate repeat if rollover key was released.
+
+@param	aScanCode	Scan code
+*/
 void CKeyboardRepeat::KeyUp(TInt aScanCode)
 	{
-	if (iAlternateRepeatExists && iAlternateRepeat.iScanCode==aScanCode)
+	if (iAlternateRepeatExists && iAlternateRepeat.iInputScanCode == aScanCode)
 		iAlternateRepeatExists=EFalse;
-	if (iRepeating!=ERepeatNone && iCurrentRepeat.iScanCode==aScanCode)
+	if (iRepeating != ERepeatNone && iCurrentRepeat.iInputScanCode == aScanCode)
 		{
 		if (iRepeating==ERepeatLong)
 			{
 			// Defensive programming - iLongCapture should never be NULL if iRepeating >= ERepeatLong			
-			if (iLongCapture && !(iLongCapture->iData.flags&ELongCaptureShortEventImmediately))
+			WS_ASSERT_DEBUG(iLongCapture != NULL, EWsPanicKeyRepeat);
+			if (iLongCapture && !(iLongCapture->iFlags & ELongCaptureShortEventImmediately))
 				{
-				TWindowServerEvent::QueueKeyPress(iCurrentRepeat.iKey,iCurrentRepeat.iScanCode,NULL,EFalse,0);	
+				TWindowServerEvent::QueueKeyPress(iCurrentRepeat.iOutput, EFalse, 0);
 				}
 			}			
 		if (iAlternateRepeatExists)