diff -r 000000000000 -r f63038272f30 bluetoothengine/bthid/keyboard/src/keyboard.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothengine/bthid/keyboard/src/keyboard.cpp Mon Jan 18 20:28:57 2010 +0200 @@ -0,0 +1,1932 @@ +/* +* Copyright (c) 2004 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" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: This is the implementation of application class + * +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidtranslate.h" +#include "finder.h" +#include "keyboard.h" +#include "layoutmgr.h" +#include "debug.h" +#include "timeouttimer.h" +#include "layoututils.h" +#include "bthidPsKey.h" + +// ---------------------------------------------------------------------- + +// Application UIDs for finding window groups. +const TInt KIdleAppUid = 0x102750f0; +const TInt KPhoneAppUid = 0x100058b3; +const TInt KMenuAppUid = 0x101f4cd2; +const TInt KSysApUid = 0x100058f3; +const TInt KActiveNotesUid = 0x10281a31; +const TInt KMessagingUid = 0x100058C5; +const TInt KServicesUid = 0x10008D39; +//const TInt KMultimediaMenuUid = 0x10281cfb; + +// Key repeat parameters to be configured +const TInt KRepeatEndTimeout = 5000000; // 5 seconds + +const TInt KKeyRepeatDelay = 500000; +const TInt KKeyRepeatInterval = 75000; +_LIT(KAppName, "PaintCursor.exe"); +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::CHidKeyboardDriver +//---------------------------------------------------------------------------- +// +CHidKeyboardDriver::CHidKeyboardDriver(MDriverAccess* aGenericHid) : + iDriverState(EUninitialised), iGenericHid(aGenericHid), iMmKeyDown(0) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::CHidKeyboardDriver(0x%08x)"), aGenericHid)); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::NewLC +//---------------------------------------------------------------------------- +// +CHidKeyboardDriver* CHidKeyboardDriver::NewLC(MDriverAccess* aGenericHid) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::NewLC(0x%08x)"), aGenericHid)); + CHidKeyboardDriver* self = new (ELeave) CHidKeyboardDriver(aGenericHid); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::NewL +//---------------------------------------------------------------------------- +// +CHidKeyboardDriver* CHidKeyboardDriver::NewL(MDriverAccess* aGenericHid) + { + CHidKeyboardDriver* self = CHidKeyboardDriver::NewLC(aGenericHid); + CleanupStack::Pop(); + return self; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::ConstructL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::ConstructL() + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver{0x%08x}::ConstructL()"), this)); + + User::LeaveIfNull(iGenericHid); + + // Create a session with the window server: + User::LeaveIfError(iWsSession.Connect()); + + // Create a session with the layout manager: + User::LeaveIfError(iLayoutMgr.Connect()); + + // We also need a key repeat timer: + iRepeatTimer = CPeriodic::NewL(CActive::EPriorityStandard); + + // repeat ending timer + iRepeatEndTimer = CTimeOutTimer::NewL(EPriorityNormal, *this); + iAppMenuId = AppMenuId(); + iPhoneAppId = PhoneAppId(); + iIdleAppId = IdleAppId(); + + iComboDevice = EFalse; + + iSettings = CBtHidSettings::NewL(); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::~CHidKeyboardDriver +//---------------------------------------------------------------------------- +// +CHidKeyboardDriver::~CHidKeyboardDriver() + { + TRACE_INFO( (_L("[HID]\t~CHidKeyboardDriver() 0x%08x"), this)); + + CancelAllKeys(); + delete iRepeatTimer; + delete iRepeatEndTimer; + iWsSession.Close(); + iLayoutMgr.Close(); + + for (TInt i = 0; i < KNumInputFieldTypes; ++i) + { + iKeys[i].Reset(); + } + + iPointBufQueue.Close(); + + if (iComboDevice) + { + RProperty::Set( KPSUidBthidSrv, KBTMouseCursorState, ECursorHide ); + } + + if (iSettings) + delete iSettings; + } + +void CHidKeyboardDriver::MoveCursor(const TPoint& aPoint) + { + DBG(RDebug::Print( + _L("CHidKeyboard::MoveCursor"))); + + PostPointer(aPoint); + } +// --------------------------------------------------------------------------- +// CHidMouseDriver::PostPointer +// Save the event to the buffer +// --------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::PostPointer(const TPoint& aPoint) + { + DBG(RDebug::Print(_L("CHidKeyboard::PostPointer"))); + iPointerBuffer.iPoint[iPointerBuffer.iNum] = aPoint; + iPointerBuffer.iType[iPointerBuffer.iNum] = KBufferPlainPointer; + iPointerBuffer.iNum++; + TInt ret = KErrNone; + + if (iPointerBuffer.iNum > KPMaxEvent) + { + ret = iPointBufQueue.Send(iPointerBuffer); + iPointerBuffer.iNum = 0; + } + return ret; + } + +TInt CHidKeyboardDriver::SendButtonEvent(TBool aButtonDown) + { + DBG(RDebug::Print( + _L("CHidKeyboard::SendButtonEvent"))); + iPointerBuffer.iPoint[iPointerBuffer.iNum] = TPoint(0, 0); + iPointerBuffer.iType[iPointerBuffer.iNum] = aButtonDown + ? KBufferPenDown + : KBufferPenUp; + iPointerBuffer.iNum++; + TInt ret = iPointBufQueue.Send(iPointerBuffer); + iPointerBuffer.iNum = 0; + return ret; + } +//---------------------------------------------------------------------------- +// CHidKeyboardDriver:::StartL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::StartL(TInt aConnectionId) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::StartL"))); + aConnectionId = aConnectionId; + // No keys are pressed: + iModifiers = 0; + for (TInt i = 0; i < KNumInputFieldTypes; ++i) + { + if (iField[i]) + { + // Reset the keys pressed array to all zeros + RArray& keys = iKeys[i]; + + for (TInt j = 0; j < keys.Count(); j++) + { + keys[j] = 0; + } + } + } + + // Start up with Num Lock active: + iLockState = ENumLock; + SetKeyboardLeds(); + + //In case of reconnection, try to initialize the Keybord driver... + InitialiseL(aConnectionId); + + // Reset the state of the layout decoder: + iLayoutMgr.Reset(); + + TInt initialLayout; + User::LeaveIfError(iLayoutMgr.GetLayout(initialLayout)); + iLastSelectedLayout = iSettings->LoadLayoutSetting(); + TBool sameCategory = CLayoutUtils::SameCategory( + static_cast (initialLayout), + iLastSelectedLayout); + if (sameCategory) + { + //Used the layoutID from CenRep + iLayoutMgr.SetLayout(iLastSelectedLayout); + } + TInt err = iPointBufQueue.OpenGlobal(KMsgBTMouseBufferQueue); + if (err == KErrNotFound) + { + User::LeaveIfError(iPointBufQueue.CreateGlobal(KMsgBTMouseBufferQueue, KPointQueueLen)); + } + else + { + User::LeaveIfError( err ); + } + + // Ready to process keyboard events: + iDriverState = EInitialised; + + if (iComboDevice) + { + LaunchApplicationL(KAppName); + RProperty::Set( KPSUidBthidSrv, KBTMouseCursorState, ECursorShow ); + } + + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::InitialiseL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::InitialiseL(TInt aConnectionId) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::InitialiseL(%d)"), + aConnectionId)); + + // Store the connection ID: + iConnectionId = aConnectionId; + + // Initialise the layout manager: + TInt country = iGenericHid->CountryCodeL(aConnectionId); + TInt vendor = iGenericHid->VendorIdL(aConnectionId); + TInt product = iGenericHid->ProductIdL(aConnectionId); + + TRACE_INFO( ( + _L("[HID]\t Country = %d, vendor = %d, product = %d"), + country, vendor, product)); + + User::LeaveIfError(iLayoutMgr.SetInitialLayout(country, vendor, product)); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::Stop +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::Stop() + { + iDriverState = EDisabled; + CancelAllKeys(); + if (iComboDevice) + { + RProperty::Set( KPSUidBthidSrv, KBTMouseCursorState, ECursorHide ); + } + } + +//---------------------------------------------------------------------------- +// CHidMouseDriver::LaunchApplicationL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::LaunchApplicationL(const TDesC& aName) + { + //Check if application is already running in the background + TApaTaskList tasks(iWsSession); + TApaTask task = tasks.FindApp(aName); + if (task.Exists()) + { + // Application is active, so just bring to foreground + } + else + { + // If application is not running, then create a new one + CApaCommandLine* cmd = CApaCommandLine::NewLC(); + cmd->SetExecutableNameL(aName); + cmd->SetCommandL(EApaCommandBackground); // EApaCommandRun + + RApaLsSession arcSession; + //connect to AppArc server + User::LeaveIfError(arcSession.Connect()); + CleanupClosePushL(arcSession); + User::LeaveIfError(arcSession.StartApp(*cmd)); + arcSession.Close(); + CleanupStack::PopAndDestroy(2); + } + + } + +// ---------------------------------------------------------------------- +// CHidDriver mandatory functions: + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::DataIn +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::DataIn(CHidTransport::THidChannelType aChannel, + const TDesC8& aPayload) + { + TInt err = KErrNone; + switch (aChannel) + { + case CHidTransport::EHidChannelInt: + if (EInitialised == iDriverState) + { + if (iComboDevice) + { + TInt mouseStatus; + TInt err = RProperty::Get( KPSUidBthidSrv, KBTMouseCursorState, mouseStatus ); + if ( !err && (static_cast(mouseStatus) == ECursorHide) ) + { + err = RProperty::Set( KPSUidBthidSrv, KBTMouseCursorState, ECursorShow ); + } + } + InterruptData(aPayload); + } + break; + + case CHidTransport::EHidChannelCtrl: + break; + + default: + break; + } + return err; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::CommandResult +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::CommandResult(TInt /*aCmdAck*/) + { + // No implementation as we don't issue any requests to be acknowledged + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::Disconnected +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::Disconnected(TInt aReason) + { + RDebug::Print(_L("CHidKeyboardDriver::Disconnected(%d)"), aReason); + Stop(); + } + +void CHidKeyboardDriver::UpdateXY(TInt aFieldIndex, const TDesC8& aReport) + { + // DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateModifiers()"))); + + // Translate the HID usage values into a boot protocol style + // modifier bitmask: + // + TReportTranslator report(aReport, iMouseField[aFieldIndex]); + + TInt Xvalue = 0; + TInt Yvalue = 0; + + TInt errX = report.GetValue(Xvalue, EGenericDesktopUsageX); + TInt errY = report.GetValue(Yvalue, EGenericDesktopUsageY); + + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateXY (%d,%d)"), Xvalue, Yvalue)); + if ((Xvalue != 0) || (Yvalue != 0)) + { + MoveCursor(TPoint(Xvalue, Yvalue)); + } + } + +void CHidKeyboardDriver::UpdateWheel(TInt aFieldIndex, const TDesC8& aReport) + { + TReportTranslator report(aReport, iMouseField[aFieldIndex]); + + TInt Yvalue = 0; + + TInt errY = report.GetValue(Yvalue, EGenericDesktopUsageWheel); + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateWheel (%d)"), Yvalue)); + TInt absValue(Abs(Yvalue)); + if ((errY == KErrNone) && (absValue >= 1)) + { + TRawEvent rawEvent; + for (TInt ii = 0; ii < absValue; ii++) + { + rawEvent.Set(TRawEvent::EKeyDown, + (Yvalue > 0) ? EStdKeyUpArrow : EStdKeyDownArrow); + UserSvr::AddEvent(rawEvent); + rawEvent.Set(TRawEvent::EKeyUp, + (Yvalue > 0) ? EStdKeyUpArrow : EStdKeyDownArrow); + UserSvr::AddEvent(rawEvent); + } + } + DBG(RDebug::Print(_L("[HID]\t new iModifiers = %02x"), iModifiers)); + } + +void CHidKeyboardDriver::UpdateButtons(TInt aFieldIndex, + const TDesC8& aReport) + { + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateButtons()"))); + // Translate the HID usage values into a boot protocol style + // modifier bitmask: + // + const TInt KButton1 = 1; + const TInt KButton2 = 2; + const TInt KButton3 = 3; + + TBool buttonPressed(EFalse); + + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateButtons() %d, %d, %d"), + iMouseField[aFieldIndex]->UsagePage(), + iMouseField[aFieldIndex]->UsageMin(), + iMouseField[aFieldIndex]->UsageMax())); + (void) aFieldIndex; + // Hack but works + // We dont come here if the report is wrong? + TInt buttonByte = aReport[1]; + if (KButton1 == buttonByte) + { + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateButtons() Button1"))); + buttonPressed = ETrue; + } + + if (KButton2 == buttonByte) + { + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateButtons() Button2"))); + if (!iButton2Down) + { + iButton2Down = ETrue; + TRawEvent rawEvent; + rawEvent.Set(TRawEvent::EKeyDown, EStdKeyApplication0); + UserSvr::AddEvent(rawEvent); + } + } + else + { + if (iButton2Down) + { + iButton2Down = EFalse; + TRawEvent rawEvent; + rawEvent.Set(TRawEvent::EKeyUp, EStdKeyApplication0); + UserSvr::AddEvent(rawEvent); + } + } + + if (KButton3 == buttonByte) + { + DBG(RDebug::Print(_L("[HID]\tCHidMouseDriver::UpdateButtons() Button3"))); + buttonPressed = ETrue; + } + + if (buttonPressed) + { + if (!iButtonDown) + { + iButtonDown = ETrue; + SendButtonEvent(ETrue);//Send Mouse Button Down + } + } + else + { + if (iButtonDown) + { + iButtonDown = EFalse; + SendButtonEvent(EFalse); //Send Mouse Button Up + } + } + } +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::InterruptData +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::InterruptData(const TDesC8& aPayload) + { + // If the report has a report ID, it is in the first byte. + // If not, this value is ignored (see CField::IsInReport()). + // + TInt firstByte = aPayload[0]; + + TRACE_INFO( ( + _L("[HID]\tCHidKeyboardDriver::InterruptData(), report %d, length %d"), + firstByte, aPayload.Length())); +#ifdef _DEBUG + + for (TInt ii = 0; ii < aPayload.Length(); ii++) + { + TInt nextByte = aPayload[ii]; + DBG(RDebug::Print( + _L("[HID]\tCHidKeyboardDriver::InterruptData() report[%d] = %d"), + ii, nextByte)); + } +#endif + TBool mouseEvent(EFalse); + if (iMouseField[EMouseXY] && iMouseField[EMouseXY]->IsInReport(firstByte)) + { + UpdateXY(EMouseXY, aPayload); + mouseEvent = ETrue; + } + + if (iMouseField[EMouseButtons] && iMouseField[EMouseButtons]->IsInReport( + firstByte)) + { + UpdateButtons(EMouseButtons, aPayload); + mouseEvent = ETrue; + } + if (iMouseField[EMouseXY] && iMouseField[EMouseXY]->IsInReport(firstByte)) + { + UpdateWheel(EMouseWheel, aPayload); + mouseEvent = ETrue; + } + DBG(RDebug::Print(_L("[HID]\tCHidKeyboardDriver::InterruptData() mouseevent %d"), + mouseEvent)); + if (mouseEvent) + { + return; + } + // First check for any rollover errors: + // + TInt i; + for (i = 0; i < KNumInputFieldTypes; ++i) + { + if (iField[i] && iField[i]->IsInReport(firstByte) + && (iField[i]->UsagePage() == EUsagePageKeyboard)) + { + if (IsRollover(i, aPayload)) + { + CancelAllKeys(); + iLayoutMgr.Reset(); + return; + } + } + } + + // Update the modifier state: + // + if (iField[EModifierKeys] && iField[EModifierKeys]->IsInReport(firstByte)) + { + UpdateModifiers(EModifierKeys, aPayload); + } + + // Finally, look for key events in all fields in this report: + // + for (i = 0; i < KNumInputFieldTypes; ++i) + { + if (iField[i] && iField[i]->IsInReport(firstByte)) + { + ProcessKeys(i, aPayload); + } + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IsRollover +// +// IsRollover() checks to see if the keyboard is in a rollover state +// (where too many keys are pressed to be able to give a sensible +// report). +// +// Rollover can only happen for fields with usage page 0x07. +// +// Returns ETrue if the keyboard is in a rollover state +//---------------------------------------------------------------------------- +// +TBool CHidKeyboardDriver::IsRollover(TInt aFieldIndex, const TDesC8& aReport) const + { + TBool rollover = ETrue; + + TReportTranslator report(aReport, iField[aFieldIndex]); + + for (TInt i = 0; rollover && (i < report.Count()); ++i) + { + const TInt KRolloverError = 1; + TInt usage; + + if (KErrNone == report.GetUsageId(usage, i) && usage + != KRolloverError) + { + rollover = EFalse; + } + } + + return rollover; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::ProcessKeys +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::ProcessKeys(TInt aFieldIndex, const TDesC8& aReport) + { + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::ProcessKeys()"))); + + // Get the appropriate field and key press array + const CField* field = iField[aFieldIndex]; + RArray& keysPressed = iKeys[aFieldIndex]; + + TInt bufferSize = keysPressed.Count(); + TReportTranslator report(aReport, field); + + // 1. For each key that appeared in the last report but does + // not appear in this report, send a key UP event. + + TRACE_FUNC + ((_L("[HID]\t Testing for keys up"))); + + TInt i; + for (i = 0; i < bufferSize; ++i) + { + TInt key = keysPressed[i]; + + if (key) + { + TInt pressed = EFalse; + + if ((report.GetValue(pressed, key) == KErrNone) && (!pressed)) + { + TRACE_INFO( (_L("[HID]\t Key up: %d [0x%04x]"), key, key)); + KeyUp(key, field->UsagePage()); + } + } + } + + // 2. For each key that appears in this report, but does + // not appear in the last report, send a key DOWN event. + + TRACE_FUNC + ((_L(" Testing for keys down"))); + + for (i = 0; i < report.Count(); ++i) + { + TInt key; + + if ((KErrNone == report.GetUsageId(key, i)) && (key != 0)) + { + // Was the key present (pressed) in the last report? + TBool match = EFalse; + + for (TInt j = 0; !match && (j < bufferSize); ++j) + { + match = (key == keysPressed[j]); + } + + // If it's a newly-pressed key then send a KeyDown + if (!match) + { + TRACE_INFO( (_L("[HID]\t Key down: %d [0x%02x]"), key, key)); + if (iInputHandlingReg->AllowedToHandleEvent( + EUsagePageKeyboard, key)) + { + iInputHandlingReg->AddHandledEvent( + EUsagePageGenericDesktop, key); + KeyDown(key, field->UsagePage()); + UpdateLockState(key); + } + } + } + } + + // 3. Finally, record what keys are down: + + TInt index = 0; + for (i = 0; (i < report.Count()) && (index < bufferSize); ++i) + { + TInt key; + + if (KErrNone == report.GetUsageId(key, i) && key != 0) + { + keysPressed[index++] = key; + } + } + while (index < bufferSize) + { + keysPressed[index++] = 0; + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::KeyEvent +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::KeyEvent(TBool aIsKeyDown, TInt aHidKey, + TInt aUsagePage) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::KeyEvent(%d, %d, %d)"), aIsKeyDown, + aUsagePage, aHidKey)); + + // Any type of key event will cancel a repeating key: + // + if (iRepeatTimer->IsActive()) + { + iRepeatTimer->Cancel(); + } + + if (iRepeatEndTimer->IsActive()) + { + iRepeatEndTimer->Cancel(); + } + + // Pass the key info to the keyboard layout translator: + // + TDecodedKeyInfo decodedKeys; + TInt err = iLayoutMgr.KeyEvent(aIsKeyDown, aHidKey, aUsagePage, + iModifiers, TLockKeys(iLockState & ECapsLock, iLockState + & ENumLock), decodedKeys); + + TTranslatedKey& key = decodedKeys.iEvent[0]; + if (err == KErrNone) + { + // Send the "key down" or "key up" event to the system: + // Handle special application launch key combinations + TRAPD( err, HandleApplicationLaunchKeysL(decodedKeys.iScanCode, aIsKeyDown, iModifiers)); + if (KErrNone != err) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::KeyEvent:Launch failed"))); + } + + //check if the key is multimedia key that needs to be sent as RawEvent and send it. + // if key event is consumed don't send it anymore. + if (!HandleMultimediaKeys(decodedKeys.iScanCode, aIsKeyDown, + iModifiers)) //check if the key is multimedia key that needs to be sent as RawEvent and send it. + { + if (decodedKeys.iScanCode != EStdKeyNull) + { + TKeyEvent event = + TKeyEventFromScanCode(decodedKeys.iScanCode); + + // remove other modifiers than autorepeat. + if (event.iScanCode == EStdKeyDevice3) + { + event.iModifiers &= EModifierAutorepeatable; + } + + //Number key events to phone app are handled differently. + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::KeyEvent: ScanCode: %d %c"), decodedKeys.iScanCode, decodedKeys.iScanCode)); + TInt unikey = key.iUnicode; + //Send key codes differently to idle app + if ((IsDigitKey(decodedKeys.iScanCode) || IsSpecialHandleKey( + unikey) || decodedKeys.iScanCode == EStdKeyYes + || decodedKeys.iScanCode == EStdKeyBackspace) + && IsPhoneAppTopMost()) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::KeyEvent: Send event %c to idle editor"), unikey)); + HandleTelephoneAppKeys(decodedKeys.iScanCode, unikey, + aIsKeyDown); + } + else + SendToWindowGroup(event, aIsKeyDown + ? EEventKeyDown + : EEventKeyUp); + } + + // Send any "key press" events to the system, if applicable: + // + for (TInt i = 0; i < decodedKeys.iCount; ++i) + { + TTranslatedKey & key = decodedKeys.iEvent[i]; + + SendKeyPress(key.iUnicode, key.iUsagePage, key.iScanCode, + key.iIsRepeatingKey); + } + } + } + else + { + TRACE_FUNC + ((_L("[HID]\tKeyboard event translation failed!"))); + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::TKeyEventFromScanCode +//---------------------------------------------------------------------------- +// +TKeyEvent CHidKeyboardDriver::TKeyEventFromScanCode(TInt aScanCode) const + { + TKeyEvent event; + + event.iScanCode = aScanCode; + event.iModifiers = KeyEventModifiers(); + + // The following are always zero for a key down or key up event: + event.iCode = 0; + event.iRepeats = 0; + + return event; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IsDigitKey +//---------------------------------------------------------------------------- +// +TBool CHidKeyboardDriver::IsDigitKey(TInt aScanCode) + { + TBool ret = EFalse; + + if (aScanCode >= '0' && aScanCode <= '9') + { + ret = ETrue; + } + + return ret; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IsDigit +//---------------------------------------------------------------------------- +// +TBool CHidKeyboardDriver::IsSpecialHandleKey(TInt aUniCode) + { + TBool ret = EFalse; + + if (aUniCode == '+' || aUniCode == '*' || aUniCode == '#' || aUniCode + == 'w' || aUniCode == 'p') + { + ret = ETrue; + } + + return ret; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::HandleTelephoneAppKeys +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::HandleTelephoneAppKeys(TInt aScanCode, + TInt aUniCode, TBool aIsKeyDown) + { + // Telephone view / idle view can't handle proper key events, but the key events must be sent as + // raw events. + TRawEvent rawEvent; + TUint modifier = 0; + TInt scancode = aScanCode; + switch (aUniCode) + { + case '+': + case EStdKeyNkpPlus: + scancode = EStdKeyNkpPlus; + modifier = EModifierLeftShift | EModifierShift; + break; + case '*': + //case EStdKeyNkpAsterisk: + scancode = EStdKeyNkpAsterisk; + modifier = 0; //EModifierLeftShift | EModifierShift; + break; + case '#': + //case EStdKeyHash: + scancode = EStdKeyHash; + modifier = EModifierLeftShift | EModifierShift; + break; + case 'w': + modifier = EModifierLeftShift | EModifierShift; + break; + case 'p': + modifier = EModifierLeftShift | EModifierShift; + break; + default: + break; + } + TRACE_INFO( (_L("[HID]\tHIDKeyboard: HandleTelephoneAppKeys: Overridden Scancode: %d, Modifier: %d"), scancode, modifier)); + TRACE_INFO( (_L("[HID]\tHIDKeyboard: HandleTelephoneAppKeys: Original unicode: %d"), aUniCode)); + + if (aIsKeyDown) + rawEvent.Set(TRawEvent::EKeyDown, scancode); + else + rawEvent.Set(TRawEvent::EKeyUp, scancode); //This will never be called for +,* and # + + TEventModifier ab = (TEventModifier) -1; //all bits set to 1. + TModifierState ba = (TModifierState) modifier; + TRACE_INFO( (_L("[HID]\t modifiers %d, modifierState: %d"), ab, (TUint)ba)); + + iWsSession.SetModifierState(ab, ba); + iWsSession.SimulateRawEvent(rawEvent); + iWsSession.Flush(); + TRACE_FUNC + ((_L("[HID]\tHIDKeyboard: HandleTelephoneAppKeys: Event sent!"))); + // Plus, Asterix and Hash keys need to send separate KeyUp event too. + if (aUniCode == '+' || aUniCode == '*' || aUniCode == '#' || aUniCode + == 'p' || aUniCode == 'w') + { + rawEvent.Set(TRawEvent::EKeyUp, scancode); + iWsSession.SimulateRawEvent(rawEvent); //Key up event + iWsSession.Flush(); + TRACE_FUNC + ((_L("[HID]\tHIDKeyboard: HandleTelephoneAppKeys: Separate Key up event sent!"))); + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::CancelKeysForField +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::CancelKeysForField(TInt aFieldIndex) + { + //TRACE_FUNC((_L("[HID]\tCHidKeyboardDriver::CancelKeysForField(%d)"), + if (iField[aFieldIndex]) + { + for (TInt i = 0; i < iField[aFieldIndex]->Count(); ++i) + { + TInt key = iKeys[aFieldIndex][i]; + //TRACE_FUNC((_L("[HID]\t Cancel key@%d=%02x (%d)"), + // i, key, key)); + if (key != 0) + { + KeyUp(key, iField[aFieldIndex]->UsagePage()); + iKeys[aFieldIndex][i] = 0; + } + } + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::CancelAllKeys +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::CancelAllKeys() + { + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::CancelAllKeys()"))); + + // Stop the key repeat timer: + if (iRepeatTimer->IsActive()) + { + iRepeatTimer->Cancel(); + } + + // Stop the key repeat safety timer: + if (iRepeatEndTimer->IsActive()) + { + iRepeatEndTimer->Cancel(); + } + + // Send key up events for any pressed keys: + for (TInt i = 0; i < KNumInputFieldTypes; ++i) + { + CancelKeysForField(i); + } + + // No modifier keys pressed: + iModifiers = 0; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::UpdateModifiers +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::UpdateModifiers(TInt aFieldIndex, + const TDesC8& aReport) + { + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::UpdateModifiers()"))); + TRACE_INFO( (_L("[HID]\t old iModifiers = %02x"), iModifiers)); + + const TInt KLeftControl = 224; // => bit 0 of modifier bitmask + const TInt KRightGui = 231; // => bit 7 + + TUint8 newModifiers = 0; + + // Translate the HID usage values into a boot protocol style + // modifier bitmask: + // + TReportTranslator report(aReport, iField[aFieldIndex]); + for (TInt i = KLeftControl; i <= KRightGui; i++) + { + TInt value; + + if (KErrNone == report.GetValue(value, i) && value) + { + newModifiers |= (1 << (i - KLeftControl)); + } + } + + // Finally, record the new state: + // + iModifiers = newModifiers; + + TRACE_INFO( (_L("[HID]\t new iModifiers = %02x"), iModifiers)); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::UpdateLockState +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::UpdateLockState(TInt aHidKey) + { + const TInt KHidCapsLock = 57; + const TInt KHidScrollLock = 71; + const TInt KHidNumLock = 83; + + TUint oldLockState = iLockState; + + switch (aHidKey) + { + case KHidNumLock: + iLockState ^= ENumLock; + break; + case KHidCapsLock: + iLockState ^= ECapsLock; + break; + case KHidScrollLock: + iLockState ^= EScrollLock; + break; + } + + if (oldLockState != iLockState) + { + TRACE_INFO( (_L("[HID]\tChanged lock state: %d -> %d"), + oldLockState, iLockState)); + SetKeyboardLeds(); + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::KeyEventModifiers +//---------------------------------------------------------------------------- +// +TUint32 CHidKeyboardDriver::KeyEventModifiers() const + { + // Modifier state: + + const TInt KNumModifierKeys = 8; + + const TUint32 KModifierMask[KNumModifierKeys] = + { + EModifierLeftCtrl | EModifierCtrl, // 0x A0 (report bit 0) + EModifierLeftShift | EModifierShift, // 0x 500 + EModifierLeftAlt | EModifierAlt, // 0x 14 + EModifierLeftFunc | EModifierFunc, // 0x2800 + EModifierRightCtrl | EModifierCtrl, // 0x C0 + EModifierRightShift | EModifierShift, // 0x 600 + EModifierRightAlt | EModifierAlt, // 0x 18 + EModifierRightFunc | EModifierFunc + // 0x3000 (report bit 7) + }; + + TUint32 result = 0; + + TUint bitPosn = 1; + for (TInt i = 0; i < KNumModifierKeys; ++i) + { + if (iModifiers & bitPosn) + { + result |= KModifierMask[i]; + } + bitPosn <<= 1; + } + + // Lock state: + + if (iLockState & ECapsLock) + { + result |= EModifierCapsLock; // 0x 4000 + } + + if (iLockState & ENumLock) + { + result |= EModifierNumLock; // 0x 8000 + } + + if (iLockState & EScrollLock) + { + result |= EModifierScrollLock; // 0x10000 + } + + // Flag used by the FEP to identify events from the HID keyboard + const TUint KHidKeyboardFepFlag = 0x00200000; + result |= KHidKeyboardFepFlag; + + return result; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SetKeyboardLeds +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::SetKeyboardLeds() const + { + // Not supported at present + } + +// ---------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::OnKeyRepeat +// Key repeat call-back: +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::OnKeyRepeat() + { + // Send another key-press event: + iLastKey.iRepeats = 1; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::OnKeyRepeat() event [%d, %d, 0x%06x, %d]"), + iLastKey.iCode, iLastKey.iScanCode, + iLastKey.iModifiers, iLastKey.iRepeats)); + SendToWindowGroup(iLastKey, EEventKey); + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CBTRCCVolumeLevelController::VolumePSChangeTimedoutCallback +// ----------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::TimerFiredOnKeyRepeat(TAny* aThis) + { + return reinterpret_cast (aThis)->OnKeyRepeat(); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SendKeyPress +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::SendKeyPress(TUint16 aUnicodeKey, TInt aUsagePage, + TInt aScanCode, TBool aIsRepeatingKey) + { + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendKeyPress([%d], %d, %d)"), + aUnicodeKey, aScanCode, aIsRepeatingKey)); + +#ifdef _DEBUG + //Please the compiler + //TBuf<256> winGroupName; + //iWsSession.GetWindowGroupNameFromIdentifier(iWsSession.GetFocusWindowGroup(), winGroupName); + //TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver: Focused Window group:<%S>"), winGroupName)); +#endif + + //Number entry to Telephone app is handled differently. + if ((IsDigitKey(aScanCode) || IsSpecialHandleKey(aUnicodeKey)) + && IsPhoneAppTopMost()) + return; + + TKeyEvent event = TKeyEventFromScanCode(aScanCode); + event.iCode = aUnicodeKey; + + if (aIsRepeatingKey) + { + event.iModifiers |= EModifierAutorepeatable; + } + + // Cancel any current key repeat: + if (iRepeatTimer->IsActive()) + { + iRepeatTimer->Cancel(); + } + + // Send the key press event: + + const TInt KConsumerControlPage = 0x0c; + + // Number entry in telephony does not activate unless we send the key event through WindowServer. + if (aUsagePage == KConsumerControlPage) + + { + // Send media keys using RWsSession::SimulateKeyEvent() so + // that the window server "capture key" function will + // operate. Note that this means that media keys won't have + // standard repeat key functionality because we can't set + // the iRepeats member using SimulateKeyEvent(). + SendToWindowServer(event); + } + else + { + //If Chr-key, send '*' to open Special Character Dialog, but not if idle app (telephone). + if (event.iCode == EKeyApplication2) + { + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::SendKeyPress, AltGr pressed"))); + if (IsPhoneAppTopMost()) //If idle app top-most, don't send '*'. + return; + event.iCode = '*'; + event.iScanCode = EStdKeyNkpAsterisk; + event.iModifiers = 0; + } + //If Enter-key, check if AppMenu or Phone/Idle is topmost --> make it Selection key! + if (event.iCode == EKeyEnter) + { + if (IsApplicationMenuTopMost() || IsPhoneAppTopMost()) + { + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::SendKeyPress, Change enter to selection"))); + event.iCode = EKeyDevice3; + event.iScanCode = EStdKeyDevice3; + } + } + + // remove other modifiers than autorepeat. + if (event.iScanCode == EStdKeyDevice3) + { + event.iModifiers &= EModifierAutorepeatable; + } + + // Send all other keys using RWsSession::SendEventToWindowGroup(): + SendToWindowGroup(event, EEventKey); + + // Set up a key repeat timer, if necessary: + if (aIsRepeatingKey) + { + iLastKey = event; + TTimeIntervalMicroSeconds32 KDefaultDelay = KKeyRepeatDelay; + TTimeIntervalMicroSeconds32 KDefaultRate = KKeyRepeatInterval; + iRepeatTimer->Start(KDefaultDelay, KDefaultRate, TCallBack( + CHidKeyboardDriver::TimerFiredOnKeyRepeat, this)); + if (!iRepeatEndTimer->IsActive()) + { + DBG(RDebug::Print(_L("[HID]\tCHidKeyboardDriver::SendKeyPress, start repeat ending timer"))); + iRepeatEndTimer->After(KRepeatEndTimeout); + } + } + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IsPhoneAppTopMost +// Check if Application Menu is the active application receiving the key presses +//---------------------------------------------------------------------------- +// +TBool CHidKeyboardDriver::IsApplicationMenuTopMost() + { + TBool ret = EFalse; + if (iAppMenuId == KErrNotFound) //was not found earlier in Constructor (WKB app launched at boot) + iAppMenuId = AppMenuId(); //if in constructor the AppMenu was not active, then ask is it now + TInt focusedId = iWsSession.GetFocusWindowGroup(); + if (focusedId == iAppMenuId) + ret = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::IsApplicationMenuTopMost(): %d"), ret )); + return ret; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IsPhoneAppTopMost +//---------------------------------------------------------------------------- +// +TBool CHidKeyboardDriver::IsPhoneAppTopMost() + { + TBool ret = EFalse; + if (iPhoneAppId == KErrNotFound) //was not found earlier in Constructor (WKB app launched at boot) + iPhoneAppId = PhoneAppId(); //if in constructor the AppMenu was not active, then ask is it now + if (iIdleAppId == KErrNotFound) + iIdleAppId = IdleAppId(); + + TInt focusedId = iWsSession.GetFocusWindowGroup(); + if (focusedId == iPhoneAppId || focusedId == iIdleAppId) //KPhoneIdleViewUid || focusedId == KIdleAppUid) + ret = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::IsPhoneAppTopMost(): %d"), ret )); + return ret; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::AppMenuId +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::AppMenuId() + { + TApaTaskList taskList(iWsSession); + TApaTask task = taskList.FindApp(TUid::Uid(KMenuAppUid)); + TInt Id = task.WgId(); + DBG(if (KErrNotFound == Id) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::AppMenuId(): Menu not found!"))); + + return Id; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::PhoneAppId +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::PhoneAppId() + { + + TApaTaskList taskList(iWsSession); + TApaTask task = taskList.FindApp(TUid::Uid(KPhoneAppUid)); + TInt Id = task.WgId(); + DBG(if (KErrNotFound == Id) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::PhoneAppId(): Phone not found!"))); + + return Id; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::IdleAppId +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::IdleAppId() + { + TApaTaskList taskList(iWsSession); + TApaTask task = taskList.FindApp(TUid::Uid(KIdleAppUid)); + TInt Id = task.WgId(); + DBG(if (KErrNotFound == Id) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::IdleAppId(): Idle not found!"))); + + return Id; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SendToWindowServer +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::SendToWindowServer(TKeyEvent aKeyEvent) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendToWindowServer, event: kc 0x%08x, sc 0x%08x, mod 0x%06x, rep %d]"), + aKeyEvent.iCode, aKeyEvent.iScanCode, + aKeyEvent.iModifiers, aKeyEvent.iRepeats)); + + // Prevent the window server generating key repeats as we do this ourselves + aKeyEvent.iModifiers &= ~EModifierAutorepeatable; + iWsSession.SimulateKeyEvent(aKeyEvent); + iWsSession.Flush(); + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::SendToWindowServer, Event sent!"))); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SendToWindowGroup +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::SendToWindowGroup(const TKeyEvent& aKeyEvent, + TEventCode aType) + { + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendToWindowGroup, Event: type %d, kc 0x%08x, sc 0x%08x, mod 0x%06x, rep %d]"), + aType, aKeyEvent.iCode, aKeyEvent.iScanCode, + aKeyEvent.iModifiers, aKeyEvent.iRepeats)); + + // Using RWsSession::SendEventToWindowGroup() bypasses the normal + // key handling in the window server, and so capture keys don't + // work. Therefore we check for the known captured keys in + // WindowGroupForKeyEvent(). + + TInt wGroup = WindowGroupForKeyEvent(aKeyEvent, aType); + + if (0 < wGroup) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendToWindowGroup: Send event to window group %d"), wGroup)); + + TWsEvent event; + + event.SetType(aType); + event.SetHandle(0); + + *(event.Key()) = aKeyEvent; + + // Remove shift modifiers from call key events because + // the phone application ignores them otherwise + // + if (EStdKeyYes == aKeyEvent.iScanCode || EStdKeyNo + == aKeyEvent.iScanCode) + { + event.Key()->iModifiers &= ~(EModifierLeftShift + | EModifierRightShift | EModifierShift); + } + + // don't send ctrl and shift keys. + if (EStdKeyLeftShift != aKeyEvent.iScanCode && EStdKeyRightShift + != aKeyEvent.iScanCode && EStdKeyLeftCtrl + != aKeyEvent.iScanCode && EStdKeyRightCtrl + != aKeyEvent.iScanCode) + { + iWsSession.SendEventToWindowGroup(wGroup, event); + iWsSession.Flush(); + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::SendToWindowGroup: Event sent!"))); + } + else + TRACE_FUNC + ((_L("[HID]\tCHidKeyboardDriver::SendToWindowGroup: Shift or ctrl not sent!"))); + + } + + // Keypresses should reset the backlight timer: + User::ResetInactivityTime(); + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::WindowGroupForKeyEvent +// Determine which window group a key event should be sent to. +// Except for special cases this is the window group with the focus. +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::WindowGroupForKeyEvent(const TKeyEvent& aKeyEvent, + TEventCode aType) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::WindowGroupForKeyEvent: type %d, kc 0x%08x, sc 0x%08x, mod 0x%06x, rep %d]"), + aType, aKeyEvent.iCode, aKeyEvent.iScanCode, + aKeyEvent.iModifiers, aKeyEvent.iRepeats)); + _LIT(KBackDrop, "*EiksrvBackdrop*"); + + if (EStdKeyApplication0 == aKeyEvent.iScanCode && (EEventKeyDown == aType + || EEventKeyUp == aType)) + { + // Application key up/down events go to the Eikon server + // Use this old way for application key + TInt result = iWsSession.FindWindowGroupIdentifier(0, KBackDrop); //This was in A2.x __EIKON_SERVER_NAME + DBG(if (KErrNotFound == result) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::WindowGroupForKeyEvent(): BackDropWindowGroup Name not found!"))); + return result; + } + + if (EKeyDevice2 == aKeyEvent.iCode && EEventKey == aType) + { + // Power key press events go to SysAp + TApaTaskList taskList( iWsSession ); + TApaTask task = taskList.FindApp( TUid::Uid( KSysApUid ) ); + TInt result = task.WgId(); + DBG(if (KErrNotFound == result) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::WindowGroupForKeyEvent(): SysApWindowGroup Name not found!"))) + ; + return result; + } + + if (EStdKeyNo == aKeyEvent.iScanCode + && (EEventKeyDown == aType || EEventKeyUp == aType)) + { + // End key up/down events go to the phone app + TApaTaskList taskList( iWsSession ); + TApaTask task = taskList.FindApp( TUid::Uid( KPhoneAppUid ) ); + TInt result = task.WgId(); + DBG(if (KErrNotFound == result) RDebug::Print(_L("[HID]\tCHidKeyboardDriver::WindowGroupForKeyEvent(): PhoneAppWindowGroup Name not found!"))) + ; + return result; + } + + // All other key events go to the window group with the keyboard focus + return iWsSession.GetFocusWindowGroup(); + } + +// ---------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::ResetArrayToSize +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::ResetArrayToSize(RArray& aArray, TInt aSize) + { + TInt err = KErrNone; + + aArray.Reset(); + + for (TInt i = 0; !err && (i < aSize); ++i) + { + err = aArray.Append(0); + } + + return err; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::CanHandleReportL +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::CanHandleReportL(CReportRoot* aReportRoot) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboard::CanHandleReport(0x%08x)"), + aReportRoot)); + iSupportedFieldCount = 0; + // Look for keyboard reports: + + THidFieldSearch search; + + TKeyboardFinder finder; + search.SearchL(aReportRoot, &finder); + iField[EStandardKeys] = finder.StandardKeyField(); + iField[EModifierKeys] = finder.ModifierKeyField(); + iLedField = finder.LedField(); + + TConsumerKeysFinder mmFinder; + search.SearchL(aReportRoot, &mmFinder); + iField[EMediaKeys] = mmFinder.ConsumerKeysField(); + + TPowerKeysFinder pwrFinder; + search.SearchL(aReportRoot, &pwrFinder); + iField[EPowerKeys] = pwrFinder.PowerKeysField(); + + for (TInt i = 0; i < KNumInputFieldTypes; ++i) + { + if (iField[i]) + { + iSupportedFieldCount++; + User::LeaveIfError(ResetArrayToSize(iKeys[i], iField[i]->Count())); + } + } + + TInt valid = KErrHidUnrecognised; + + // We only require standard and modifier key reports; the + // LED and consumer keys are optional: + + if ((iField[EStandardKeys] != 0) && (iField[EModifierKeys] != 0)) + { + valid = KErrNone; + } + + TMouseFinder mousefinder; + search.SearchL(aReportRoot, &mousefinder); + iMouseField[EMouseButtons] = mousefinder.ButtonsField(); + iMouseField[EMouseXY] = mousefinder.XYField(); + iMouseField[EMouseWheel] = mousefinder.WheelField(); + + if ((iMouseField[EMouseButtons] != 0) && (iMouseField[EMouseXY] != 0)) + { + iComboDevice = ETrue; + } + + TRACE_INFO( ( + _L("CHidKeyboard::CanHandleReport() returning %d"), valid)); + + return valid; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::HandleApplicationLaunchKeysL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::HandleApplicationLaunchKeysL(TUint16 aScancodeKey, + TBool aIsKeyDown, TUint8 aHIDModifiers) + { + // Note that aHIDModifier is not Symbian Modifier bits, but in below format: (from HID spec) + /* const TUint32 KModifierMask[KNumModifierKeys] = + { + EModifierLeftCtrl | EModifierCtrl, // 0x A0 (report bit 0) + EModifierLeftShift | EModifierShift, // 0x 500 + EModifierLeftAlt | EModifierAlt, // 0x 14 + EModifierLeftFunc | EModifierFunc, // 0x2800 + EModifierRightCtrl | EModifierCtrl, // 0x C0 + EModifierRightShift | EModifierShift, // 0x 600 + EModifierRightAlt | EModifierAlt, // 0x 18 + EModifierRightFunc | EModifierFunc // 0x3000 (report bit 7) + }; + */ + const TUint KHIDModifierAlt = 0x04; + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleApplicationLaunchKeys: scancode 0x%08x, HIDmodifiers 0x%08x"), aScancodeKey, aHIDModifiers)); + + switch (aScancodeKey) + { + // Alt-m launches MultimediaMenu + case 'm': + case 'M': + { + if (aHIDModifiers & KHIDModifierAlt) + { + TRACE_FUNC + ((_L("Launch multimediamenu"))); + //LaunchApplicationL( KMultimediaMenuUid ); + TInt err = SendRawEvent(EStdKeyApplication6, aIsKeyDown, 0); + if (KErrNone != err) + { + TRACE_FUNC + ((_L("[HID]\t Event sending failed"))); + } + } + break; + } + + // Alt-n launches ActiveNotes + case 'n': + case 'N': + { + if (aHIDModifiers & KHIDModifierAlt) + { + TRACE_FUNC + ((_L("[HID]\t Launch active notes"))); + LaunchApplicationL(KActiveNotesUid); + } + break; + } + case EStdKeyF1: + { + DBG(RDebug::Print(_L("[HID]\t Launch Web service"))); + LaunchApplicationL(KServicesUid); + } + break; + case EStdKeyF2: + { + DBG(RDebug::Print(_L("[HID]\t Launch messages"))); + LaunchApplicationL(KMessagingUid); + } + break; + default: + break; + } + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::LaunchApplicationL +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::LaunchApplicationL(TInt aAppUid) + { + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::LaunchApplication: UID 0x%08x"), aAppUid)); + + TApaTaskList taskList(iWsSession); + TUid uid = TUid::Uid(aAppUid); + TApaTask task = taskList.FindApp(uid); + if (task.Exists()) + { + // Application is active, so just bring to foreground + task.BringToForeground(); + } + else + { + RApaLsSession appArcSession; + // connect to AppArc server + User::LeaveIfError(appArcSession.Connect()); + + CleanupClosePushL(appArcSession); + + TThreadId threadID; + + // Launch the application with no params. + User::LeaveIfError(appArcSession.StartDocument(KNullDesC, uid, + threadID)); + + CleanupStack::Pop(1); // appArcSession + + appArcSession.Close(); + } + + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::HandleNokiaMultimediaKeys +// Takes some Multimedia keyevents from HID and converts them to Nokia raw events format +// to simulate as if the key presses came from phone keypad (volume +/-, play, pause, frwd, rewind) +// +// This is temporary solution that shall be replaced by "proper" RemCon Bearer solution. +// Because the keycodes used here may change in future terminal! +// Note that aHIDModifier is not Symbian Modifier bits, but in below format: (from HID spec) +// const TUint32 KModifierMask[KNumModifierKeys] = +// { +// EModifierLeftCtrl | EModifierCtrl, // 0x A0 (report bit 0) +// EModifierLeftShift | EModifierShift, // 0x 500 +// EModifierLeftAlt | EModifierAlt, // 0x 14 +// EModifierLeftFunc | EModifierFunc, // 0x2800 +// EModifierRightCtrl | EModifierCtrl, // 0x C0 +// EModifierRightShift | EModifierShift, // 0x 600 +// EModifierRightAlt | EModifierAlt, // 0x 18 +// EModifierRightFunc | EModifierFunc // 0x3000 (report bit 7) +// }; +//---------------------------------------------------------------------------- +// + +TBool CHidKeyboardDriver::HandleMultimediaKeys(TUint16 aScancodeKey, + TBool aIsKeyDown, TUint8 aHIDModifiers) + { + TBool ret = HandleMultimediaKeysForNokia(aScancodeKey, aIsKeyDown, + aHIDModifiers); + + if (!ret) + { + ret = HandleMultimediaKeysForStandard(aScancodeKey, aIsKeyDown, + aHIDModifiers); + } + return ret; + } + +void CHidKeyboardDriver::ResetBitmap(TBool aIsKeyDown, + TMmKeyDown aBitmapToReset) + { + if (aIsKeyDown) + { + iMmKeyDown = (iMmKeyDown | aBitmapToReset); + } + else + { + iMmKeyDown = (iMmKeyDown & !aBitmapToReset); + } + } + +TBool CHidKeyboardDriver::HandleMultimediaKeysForNokia(TUint16 aScancodeKey, + TBool aIsKeyDown, TUint8 aHIDModifiers) + { + const TUint KHidModifierCtrl = 0x01; + const TUint KHidModifierCtrlRight = 0x10; + const TUint KHidModifierAlt = 0x04; + + // const TUint KHidModifierAltGr = 0x64; + TInt scancode = 0; + TUint modifier = 0; + TBool isMmKey = EFalse; + + TMmKeyDown bitmapToReset; + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: scancode 0x%08x, HIDmodifiers 0x%08x"), aScancodeKey, aHIDModifiers)); + + switch (aScancodeKey) + { + + // Mappings for Nokia SU-8W + // Key down events are stored into bitmap in order to detect if keys are released in reverse order, which caused jamming. + // For example: control key released before arrow key. + + case EStdKeyUpArrow: + { + if (aHIDModifiers & (KHidModifierCtrl | KHidModifierCtrlRight) + || iMmKeyDown & EVolUp) + { + scancode = EStdKeyIncVolume; //Volume Up = Ctrl + ArrowUp + isMmKey = ETrue; + // Set or reset flag bit + + bitmapToReset = EVolUp; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Volume up %d"), aIsKeyDown)); + } + break; + } + + case EStdKeyDownArrow: + { + if (aHIDModifiers & (KHidModifierCtrl | KHidModifierCtrlRight) + || iMmKeyDown & EVolDown) + { + scancode = EStdKeyDecVolume; //Volume Down = Ctrl + ArrowDown + isMmKey = ETrue; + bitmapToReset = EVolDown; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Volume down %d"), aIsKeyDown)); + } + break; + } + + case EStdKeyRightArrow: + { + if (aHIDModifiers & KHidModifierAlt || iMmKeyDown & EPlay) + { + scancode = EStdKeyApplication2; //Play = Alt + ArrowRight + isMmKey = ETrue; + bitmapToReset = EPlay; + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Play %d"), aIsKeyDown)); + } + else if (aHIDModifiers & (KHidModifierCtrl + | KHidModifierCtrlRight) || iMmKeyDown & ENext) + { + scancode = EStdKeyApplication4; //Next = Ctrl + ArrowRight + isMmKey = ETrue; + bitmapToReset = ENext; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Next %d"), aIsKeyDown)); + } + break; + } + + case EStdKeyLeftArrow: + { + if (aHIDModifiers & KHidModifierAlt || iMmKeyDown & EStop) + { + scancode = EStdKeyApplication3; //Stop = Alt + ArrowLeft + isMmKey = ETrue; + bitmapToReset = EStop; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Stop %d"), aIsKeyDown)); + } + else if (aHIDModifiers & (KHidModifierCtrl + | KHidModifierCtrlRight) || iMmKeyDown & EPrev) + { + scancode = EStdKeyApplication5; //Prev = Ctrl + ArrowLeft + isMmKey = ETrue; + bitmapToReset = EPrev; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Prev %d"), aIsKeyDown)); + } + break; + } + + default: + break; + } + + if (isMmKey) + { + ResetBitmap(aIsKeyDown, bitmapToReset); + SendRawEvent(scancode, aIsKeyDown, modifier); + } + return isMmKey; + } + +TBool CHidKeyboardDriver::HandleMultimediaKeysForStandard( + TUint16 aScancodeKey, TBool aIsKeyDown, TUint8 aHIDModifiers) + { + TInt scancode = 0; + TUint modifier = 0; + TBool isMmKey = EFalse; + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: scancode 0x%08x, HIDmodifiers 0x%08x"), aScancodeKey, aHIDModifiers)); + //please complier... + (void) aHIDModifiers; + + switch (aScancodeKey) + { + // Mappings for standard keyboards + + case EStdKeyApplication2: //Play + { + scancode = EStdKeyApplication2; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Play %d"), aIsKeyDown)); + break; + } + + case EStdKeyApplication3: //Stop + { + scancode = EStdKeyApplication3; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Stop %d"), aIsKeyDown)); + break; + } + + case EStdKeyApplication4: //Next + { + scancode = EStdKeyApplication4; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Next %d"), aIsKeyDown)); + break; + } + + case EStdKeyApplication5: //Prev + { + scancode = EStdKeyApplication5; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Prev %d"), aIsKeyDown)); + break; + } + + case EStdKeyIncVolume: //Volume up + { + scancode = EStdKeyIncVolume; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Volume up %d"), aIsKeyDown)); + break; + } + + case EStdKeyDecVolume: //Volume down + { + scancode = EStdKeyDecVolume; + isMmKey = ETrue; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::HandleNokiaMultimediaKeys: Volume down %d"), aIsKeyDown)); + break; + } + + default: + break; + } + + if (isMmKey) + { + SendRawEvent(scancode, aIsKeyDown, modifier); + } + return isMmKey; + } + +// ---------------------------------------------------------------------- +// CHidKeyboardDriver::SendRawEvent +// Send raw key event to window server +// ---------------------------------------------------------------------- +TInt CHidKeyboardDriver::SendRawEvent(TInt aScancode, TBool aIsKeyDown, + TUint aModifier) + { + TRawEvent rawEvent; + TInt err = KErrNone; + + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendRawEvent: scancode %d, modifier: %d, keydown: %d"), aScancode, aModifier, aIsKeyDown )); + if (aScancode != 0) + { + if (aIsKeyDown) + rawEvent.Set(TRawEvent::EKeyDown, aScancode); + else + rawEvent.Set(TRawEvent::EKeyUp, aScancode); //This will never be called for +,* and # + + TEventModifier eventmodifier = (TEventModifier) -1; //all bits set to 1. + TModifierState modifierstate = (TModifierState) aModifier; + TRACE_INFO( (_L("[HID]\tCHidKeyboardDriver::SendRawEvents: modifiers %d, modifierState: %d"), aModifier, (TUint)modifierstate)); + + err = iWsSession.SetModifierState(eventmodifier, modifierstate); + iWsSession.SimulateRawEvent(rawEvent); + iWsSession.Flush(); + } + return err; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SupportedFieldCount +//---------------------------------------------------------------------------- +// +TInt CHidKeyboardDriver::SupportedFieldCount() + { + return iSupportedFieldCount; + } + +//---------------------------------------------------------------------------- +// CHidKeyboardDriver::SetInputHandlingReg +//---------------------------------------------------------------------------- +// +void CHidKeyboardDriver::SetInputHandlingReg( + CHidInputDataHandlingReg* aHandlingReg) + { + iInputHandlingReg = aHandlingReg; + } + +// ---------------------------------------------------------------------- +// CHidKeyboardDriver::TimerExpired +// Callback function from CTimeOutTimer +// ---------------------------------------------------------------------- +void CHidKeyboardDriver::TimerExpired() + { + DBG(RDebug::Print(_L("[HID]\tCHidKeyboardDriver::TimerExpired, cancel all key presses"))); + CancelAllKeys(); + } + +// End of file