diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2011-07-12 15:16:09 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2011-07-12 15:16:09 (GMT) |
commit | 647c618da0024373fd866999e7603cbf36cc855e (patch) | |
tree | 242028ddfcd853158a42183d997567f0d731de66 | |
parent | 7f889e1b6b6f01e6ff0775d41316ecb80e534abd (diff) | |
parent | 1a1b2af1ce2cf9acfda80947f04718a7dc2fff35 (diff) | |
download | Qt-647c618da0024373fd866999e7603cbf36cc855e.zip Qt-647c618da0024373fd866999e7603cbf36cc855e.tar.gz Qt-647c618da0024373fd866999e7603cbf36cc855e.tar.bz2 |
Merge branch 'master' of scm.dev.nokia.troll.no:qt/qt-symbian-staging into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/qt-symbian-staging:
Fix license header in qs60keycapture_p.h and qs60keycapture.cpp
Fixing WINSCW compile error
whitespace fixes
Enable key capture and RemCon interfaces for Qt apps on Symbian
-rw-r--r-- | src/corelib/global/qnamespace.h | 1 | ||||
-rw-r--r-- | src/corelib/global/qnamespace.qdoc | 9 | ||||
-rw-r--r-- | src/gui/kernel/qkeymapper_p.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qkeymapper_s60.cpp | 195 | ||||
-rw-r--r-- | src/gui/s60framework/qs60keycapture.cpp | 308 | ||||
-rw-r--r-- | src/gui/s60framework/qs60keycapture_p.h | 116 | ||||
-rw-r--r-- | src/gui/s60framework/qs60mainappui.cpp | 17 | ||||
-rw-r--r-- | src/gui/s60framework/s60framework.pri | 21 | ||||
-rw-r--r-- | src/plugins/bearer/symbian/symbianengine.cpp | 58 | ||||
-rw-r--r-- | src/plugins/bearer/symbian/symbianengine.h | 16 | ||||
-rw-r--r-- | tests/auto/qs60mainapplication/qs60mainapplication.pro | 1 | ||||
-rw-r--r-- | tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp | 259 |
12 files changed, 875 insertions, 128 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 99479d0..e247533 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -546,6 +546,7 @@ public: AA_S60DontConstructApplicationPanes = 8, AA_S60DisablePartialScreenInputMode = 9, AA_X11InitThreads = 10, + AA_CaptureMultimediaKeys = 11, // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index ad9e113..dc92866 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -171,6 +171,15 @@ construction in order to make Xlib calls thread-safe. This attribute must be set before QApplication is constructed. + \value AA_CaptureMultimediaKeys Enables application to receive multimedia key events + (play, next, previous etc). This includes also external sources such as headsets. + Application can not use Remote Control framework on Symbian if this attribute is + set. On Symbian, multimedia key event routing may vary between different devices. + For example, application on background may receive multimedia key events only if + it has active audio stream i.e. it is playing music or video. This attribute must + be set before QApplication is constructed. This attribute is only supported in Symbian + platform. + \omitvalue AA_AttributeCount */ diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h index 2607c2a..1d624f9 100644 --- a/src/gui/kernel/qkeymapper_p.h +++ b/src/gui/kernel/qkeymapper_p.h @@ -213,6 +213,8 @@ public: int mapS60ScanCodesToQt(TUint s60key); int mapQtToS60Key(int qtKey); int mapQtToS60ScanCodes(int qtKey); + int mapS60RemConIdToS60Key(int s60RemConId); + int mapS60RemConIdToS60ScanCodes(int s60RemConId); void updateInputLanguage(); #endif }; diff --git a/src/gui/kernel/qkeymapper_s60.cpp b/src/gui/kernel/qkeymapper_s60.cpp index 1113b77..2a7083e 100644 --- a/src/gui/kernel/qkeymapper_s60.cpp +++ b/src/gui/kernel/qkeymapper_s60.cpp @@ -87,97 +87,104 @@ QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers / } #include <e32keys.h> +#include <remconcoreapi.h> struct KeyMapping{ TKeyCode s60KeyCode; TStdScanCode s60ScanCode; + TRemConCoreApiOperationId s60RemConId; Qt::Key qtKey; }; using namespace Qt; +// key mapping table in the format of +// {S60 key code, S60 scan code, S60 RemCon operation Id, Qt key code} static const KeyMapping keyMapping[] = { - {EKeyBackspace, EStdKeyBackspace, Key_Backspace}, - {EKeyTab, EStdKeyTab, Key_Tab}, - {EKeyEnter, EStdKeyEnter, Key_Enter}, - {EKeyEscape, EStdKeyEscape, Key_Escape}, - {EKeySpace, EStdKeySpace, Key_Space}, - {EKeyDelete, EStdKeyDelete, Key_Delete}, - {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq}, - {EKeyPause, EStdKeyPause, Key_Pause}, - {EKeyHome, EStdKeyHome, Key_Home}, - {EKeyEnd, EStdKeyEnd, Key_End}, - {EKeyPageUp, EStdKeyPageUp, Key_PageUp}, - {EKeyPageDown, EStdKeyPageDown, Key_PageDown}, - {EKeyInsert, EStdKeyInsert, Key_Insert}, - {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left}, - {EKeyRightArrow, EStdKeyRightArrow, Key_Right}, - {EKeyUpArrow, EStdKeyUpArrow, Key_Up}, - {EKeyDownArrow, EStdKeyDownArrow, Key_Down}, - {EKeyLeftShift, EStdKeyLeftShift, Key_Shift}, - {EKeyRightShift, EStdKeyRightShift, Key_Shift}, - {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt}, - {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr}, - {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control}, - {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control}, - {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L}, - {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R}, - {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock}, - {EKeyNumLock, EStdKeyNumLock, Key_NumLock}, - {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock}, - {EKeyF1, EStdKeyF1, Key_F1}, - {EKeyF2, EStdKeyF2, Key_F2}, - {EKeyF3, EStdKeyF3, Key_F3}, - {EKeyF4, EStdKeyF4, Key_F4}, - {EKeyF5, EStdKeyF5, Key_F5}, - {EKeyF6, EStdKeyF6, Key_F6}, - {EKeyF7, EStdKeyF7, Key_F7}, - {EKeyF8, EStdKeyF8, Key_F8}, - {EKeyF9, EStdKeyF9, Key_F9}, - {EKeyF10, EStdKeyF10, Key_F10}, - {EKeyF11, EStdKeyF11, Key_F11}, - {EKeyF12, EStdKeyF12, Key_F12}, - {EKeyF13, EStdKeyF13, Key_F13}, - {EKeyF14, EStdKeyF14, Key_F14}, - {EKeyF15, EStdKeyF15, Key_F15}, - {EKeyF16, EStdKeyF16, Key_F16}, - {EKeyF17, EStdKeyF17, Key_F17}, - {EKeyF18, EStdKeyF18, Key_F18}, - {EKeyF19, EStdKeyF19, Key_F19}, - {EKeyF20, EStdKeyF20, Key_F20}, - {EKeyF21, EStdKeyF21, Key_F21}, - {EKeyF22, EStdKeyF22, Key_F22}, - {EKeyF23, EStdKeyF23, Key_F23}, - {EKeyF24, EStdKeyF24, Key_F24}, - {EKeyOff, EStdKeyOff, Key_PowerOff}, -// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0 - {EKeyHelp, EStdKeyHelp, Key_Help}, - {EKeyDial, EStdKeyDial, Key_Call}, - {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp}, - {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown}, - {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing. - {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing. - {EKeyDevice3, EStdKeyDevice3, Key_Select}, - {EKeyDevice7, EStdKeyDevice7, Key_Camera}, - {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing. - {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing. - {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing. - {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing. - {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing. - {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing. - {EKeyApplication6, EStdKeyApplication6, Key_Launch6}, - {EKeyApplication7, EStdKeyApplication7, Key_Launch7}, - {EKeyApplication8, EStdKeyApplication8, Key_Launch8}, - {EKeyApplication9, EStdKeyApplication9, Key_Launch9}, - {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA}, - {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB}, - {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC}, - {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD}, - {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE}, - {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF}, - {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus}, - {EKeyYes, EStdKeyYes, Key_Yes}, - {EKeyNo, EStdKeyNo, Key_No}, - {TKeyCode(0), TStdScanCode(0), Qt::Key(0)} + {EKeyBackspace, EStdKeyBackspace, ENop, Key_Backspace}, + {EKeyTab, EStdKeyTab, ENop, Key_Tab}, + {EKeyEnter, EStdKeyEnter, ERemConCoreApiEnter, Key_Enter}, + {EKeyEscape, EStdKeyEscape, ENop, Key_Escape}, + {EKeySpace, EStdKeySpace, ENop, Key_Space}, + {EKeyDelete, EStdKeyDelete, ENop, Key_Delete}, + {EKeyPrintScreen, EStdKeyPrintScreen, ENop, Key_SysReq}, + {EKeyPause, EStdKeyPause, ENop, Key_Pause}, + {EKeyHome, EStdKeyHome, ENop, Key_Home}, + {EKeyEnd, EStdKeyEnd, ENop, Key_End}, + {EKeyPageUp, EStdKeyPageUp, ERemConCoreApiPageUp, Key_PageUp}, + {EKeyPageDown, EStdKeyPageDown, ERemConCoreApiPageDown, Key_PageDown}, + {EKeyInsert, EStdKeyInsert, ENop, Key_Insert}, + {EKeyLeftArrow, EStdKeyLeftArrow, ERemConCoreApiLeft, Key_Left}, + {EKeyRightArrow, EStdKeyRightArrow, ERemConCoreApiRight, Key_Right}, + {EKeyUpArrow, EStdKeyUpArrow, ERemConCoreApiUp, Key_Up}, + {EKeyDownArrow, EStdKeyDownArrow, ERemConCoreApiDown, Key_Down}, + {EKeyLeftShift, EStdKeyLeftShift, ENop, Key_Shift}, + {EKeyRightShift, EStdKeyRightShift, ENop, Key_Shift}, + {EKeyLeftAlt, EStdKeyLeftAlt, ENop, Key_Alt}, + {EKeyRightAlt, EStdKeyRightAlt, ENop, Key_AltGr}, + {EKeyLeftCtrl, EStdKeyLeftCtrl, ENop, Key_Control}, + {EKeyRightCtrl, EStdKeyRightCtrl, ENop, Key_Control}, + {EKeyLeftFunc, EStdKeyLeftFunc, ENop, Key_Super_L}, + {EKeyRightFunc, EStdKeyRightFunc, ENop, Key_Super_R}, + {EKeyCapsLock, EStdKeyCapsLock, ENop, Key_CapsLock}, + {EKeyNumLock, EStdKeyNumLock, ENop, Key_NumLock}, + {EKeyScrollLock, EStdKeyScrollLock, ENop, Key_ScrollLock}, + {EKeyF1, EStdKeyF1, ERemConCoreApiF1, Key_F1}, + {EKeyF2, EStdKeyF2, ERemConCoreApiF2, Key_F2}, + {EKeyF3, EStdKeyF3, ERemConCoreApiF3, Key_F3}, + {EKeyF4, EStdKeyF4, ERemConCoreApiF4, Key_F4}, + {EKeyF5, EStdKeyF5, ERemConCoreApiF5, Key_F5}, + {EKeyF6, EStdKeyF6, ENop, Key_F6}, + {EKeyF7, EStdKeyF7, ENop, Key_F7}, + {EKeyF8, EStdKeyF8, ENop, Key_F8}, + {EKeyF9, EStdKeyF9, ENop, Key_F9}, + {EKeyF10, EStdKeyF10, ENop, Key_F10}, + {EKeyF11, EStdKeyF11, ENop, Key_F11}, + {EKeyF12, EStdKeyF12, ENop, Key_F12}, + {EKeyF13, EStdKeyF13, ENop, Key_F13}, + {EKeyF14, EStdKeyF14, ENop, Key_F14}, + {EKeyF15, EStdKeyF15, ENop, Key_F15}, + {EKeyF16, EStdKeyF16, ENop, Key_F16}, + {EKeyF17, EStdKeyF17, ENop, Key_F17}, + {EKeyF18, EStdKeyF18, ENop, Key_F18}, + {EKeyF19, EStdKeyF19, ENop, Key_F19}, + {EKeyF20, EStdKeyF20, ENop, Key_F20}, + {EKeyF21, EStdKeyF21, ENop, Key_F21}, + {EKeyF22, EStdKeyF22, ENop, Key_F22}, + {EKeyF23, EStdKeyF23, ENop, Key_F23}, + {EKeyF24, EStdKeyF24, ENop, Key_F24}, + {EKeyOff, EStdKeyOff, ENop, Key_PowerOff}, +// {EKeyMenu, EStdKeyMenu, ENop, Key_Menu}, // Menu is EKeyApplication0 + {EKeyHelp, EStdKeyHelp, ERemConCoreApiHelp, Key_Help}, + {EKeyDial, EStdKeyDial, ENop, Key_Call}, + {EKeyIncVolume, EStdKeyIncVolume, ERemConCoreApiVolumeUp, Key_VolumeUp}, + {EKeyDecVolume, EStdKeyDecVolume, ERemConCoreApiVolumeDown, Key_VolumeDown}, + {EKeyDevice0, EStdKeyDevice0, ENop, Key_Context1}, // Found by manual testing. + {EKeyDevice1, EStdKeyDevice1, ENop, Key_Context2}, // Found by manual testing. + {EKeyDevice3, EStdKeyDevice3, ERemConCoreApiSelect, Key_Select}, + {EKeyDevice7, EStdKeyDevice7, ENop, Key_Camera}, + {EKeyApplication0, EStdKeyApplication0, ENop, Key_Menu}, // Found by manual testing. + {EKeyApplication1, EStdKeyApplication1, ENop, Key_Launch1}, // Found by manual testing. + {EKeyApplication2, EStdKeyApplication2, ERemConCoreApiPlay, Key_MediaPlay}, // Found by manual testing. + {EKeyApplication3, EStdKeyApplication3, ERemConCoreApiStop, Key_MediaStop}, // Found by manual testing. + {EKeyApplication4, EStdKeyApplication4, ERemConCoreApiForward, Key_MediaNext}, // Found by manual testing. + {EKeyApplication5, EStdKeyApplication5, ERemConCoreApiBackward, Key_MediaPrevious}, // Found by manual testing. + {EKeyApplication6, EStdKeyApplication6, ENop, Key_Launch6}, + {EKeyApplication7, EStdKeyApplication7, ENop, Key_Launch7}, + {EKeyApplication8, EStdKeyApplication8, ENop, Key_Launch8}, + {EKeyApplication9, EStdKeyApplication9, ENop, Key_Launch9}, + {EKeyApplicationA, EStdKeyApplicationA, ENop, Key_LaunchA}, + {EKeyApplicationB, EStdKeyApplicationB, ENop, Key_LaunchB}, + {EKeyApplicationC, EStdKeyApplicationC, ENop, Key_LaunchC}, + {EKeyApplicationD, EStdKeyApplicationD, ENop, Key_LaunchD}, + {EKeyApplicationE, EStdKeyApplicationE, ENop, Key_LaunchE}, + {EKeyApplicationF, EStdKeyApplicationF, ENop, Key_LaunchF}, + {EKeyApplication19, EStdKeyApplication19, ENop, Key_CameraFocus}, + {EKeyYes, EStdKeyYes, ENop, Key_Yes}, + {EKeyNo, EStdKeyNo, ENop, Key_No}, + {EKeyDevice20, EStdKeyDevice20, ERemConCoreApiPausePlayFunction, Key_MediaTogglePlayPause}, + {EKeyDevice21, EStdKeyDevice21, ERemConCoreApiRewind, Key_AudioRewind}, + {EKeyDevice22, EStdKeyDevice22, ERemConCoreApiFastForward, Key_AudioForward}, + {TKeyCode(0), TStdScanCode(0), ENop, Qt::Key(0)} }; int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key) @@ -228,6 +235,30 @@ int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey) return res; } +int QKeyMapperPrivate::mapS60RemConIdToS60Key(int s60RemConId) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60RemConId == s60RemConId) { + res = keyMapping[i].s60KeyCode; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapS60RemConIdToS60ScanCodes(int s60RemConId) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60RemConId == s60RemConId) { + res = keyMapping[i].s60ScanCode; + break; + } + } + return res; +} + void QKeyMapperPrivate::updateInputLanguage() { #ifdef Q_WS_S60 diff --git a/src/gui/s60framework/qs60keycapture.cpp b/src/gui/s60framework/qs60keycapture.cpp new file mode 100644 index 0000000..415c3e1 --- /dev/null +++ b/src/gui/s60framework/qs60keycapture.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Symbian application wrapper of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <remconinterfaceselector.h> +#include <remconcoreapitarget.h> +#include <coemain.h> +#include "qkeymapper_p.h" +#include "qs60keycapture_p.h" + +QT_BEGIN_NAMESPACE + +/* + * Helper class for sending key handling responses to RemCon. +*/ +class CResponseHandler : public CActive +{ +public: + static CResponseHandler *NewL(CRemConCoreApiTarget &remConCoreApiTarget); + virtual ~CResponseHandler(); + void CompleteAnyKey(TRemConCoreApiOperationId operationId); + +private: + CResponseHandler(CRemConCoreApiTarget &remConCoreApiTarget); + + void RunL(); + void DoCancel(); + +private: + RArray<TRemConCoreApiOperationId> iResponseArray; + CRemConCoreApiTarget &iRemConCoreApiTarget; +}; + +CResponseHandler::CResponseHandler(CRemConCoreApiTarget &remConCoreApiTarget) + : CActive(CActive::EPriorityStandard), + iRemConCoreApiTarget(remConCoreApiTarget) +{ + CActiveScheduler::Add(this); +} + +CResponseHandler *CResponseHandler::NewL(CRemConCoreApiTarget &remConCoreApiTarget) +{ + CResponseHandler *self = + new (ELeave) CResponseHandler(remConCoreApiTarget); + return self; +} + +CResponseHandler::~CResponseHandler() +{ + Cancel(); + iResponseArray.Close(); +} + +void CResponseHandler::CompleteAnyKey(TRemConCoreApiOperationId operationId) +{ + if (!IsActive()) { + TInt error = KErrNone; + iRemConCoreApiTarget.SendResponse(iStatus, operationId, error); + SetActive(); + } else { + // already active. Append to array and complete later. + iResponseArray.Append(operationId); + } +} + +void CResponseHandler::DoCancel() +{ + iRemConCoreApiTarget.Cancel(); +} + +void CResponseHandler::RunL() +{ + // if any existing -> Send response + if (iResponseArray.Count()) { + CompleteAnyKey(iResponseArray[0]); + // Remove already completed key + iResponseArray.Remove(0); + iResponseArray.Compress(); + } +} + + +/* + * QS60KeyCapture provides media key handling using services from RemCon. + */ +QS60KeyCapture::QS60KeyCapture(CCoeEnv *env, QObject *parent): + QObject(parent), coeEnv(env), selector(0), target(0), handler(0) +{ + initRemCon(); + + TTimeIntervalMicroSeconds32 initialTime; + TTimeIntervalMicroSeconds32 time; + coeEnv->WsSession().GetKeyboardRepeatRate(initialTime, time); + initialRepeatTime = (initialTime.Int() / 1000); // msecs + repeatTime = (time.Int() / 1000); // msecs + + int clickTimeout = initialRepeatTime + repeatTime; + + volumeUpClickTimer.setSingleShot(true); + volumeDownClickTimer.setSingleShot(true); + repeatTimer.setSingleShot(true); + + volumeUpClickTimer.setInterval(clickTimeout); + volumeDownClickTimer.setInterval(clickTimeout); + repeatTimer.setInterval(initialRepeatTime); + + connect(&volumeUpClickTimer, SIGNAL(timeout()), this, SLOT(volumeUpClickTimerExpired())); + connect(&volumeDownClickTimer, SIGNAL(timeout()), this, SLOT(volumeDownClickTimerExpired())); + connect(&repeatTimer, SIGNAL(timeout()), this, SLOT(repeatTimerExpired())); +} + +/* + * Initializes RemCon connection for receiving key events + */ +void QS60KeyCapture::initRemCon() +{ + try { + QT_TRAP_THROWING( + selector = CRemConInterfaceSelector::NewL(); + target = CRemConCoreApiTarget::NewL(*selector, *this); + handler = CResponseHandler::NewL(*target); + selector->OpenTargetL()); + } catch (const std::exception &e) { + delete handler; + delete selector; + selector = 0; + target = 0; + handler = 0; + } +} + +QS60KeyCapture::~QS60KeyCapture() +{ + delete handler; + delete selector; +} + +void QS60KeyCapture::MrccatoCommand(TRemConCoreApiOperationId operationId, + TRemConCoreApiButtonAction buttonAction) +{ + if (!target) + return; + + switch (operationId) { + // Volume up/down keys auto repeat if user hold the key long enough + case ERemConCoreApiVolumeUp: + case ERemConCoreApiVolumeDown: + { + // Side key events are sent using following scheme: + // - ButtonClick is sent first + // - if nothing happens before repeat timer expires, no further events are generated + // - if user holds the button longer, ButtonPress is sent and + // when button is finally released ButtonRelease is sent. + + QTimer &timer = (operationId == ERemConCoreApiVolumeUp) ? volumeUpClickTimer : volumeDownClickTimer; + + switch (buttonAction) { + case ERemConCoreApiButtonPress: + if (timer.isActive()) { + timer.stop(); + // force repeat event + repeatKeyEvent = mapToKeyEvent(operationId); + repeatTimerExpired(); + } else { + sendKey(operationId, QEvent::KeyPress); + repeatTimer.start(initialRepeatTime); + } + break; + case ERemConCoreApiButtonRelease: + timer.stop(); + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + case ERemConCoreApiButtonClick: + if (timer.isActive()) { + timer.stop(); + sendKey(operationId, QEvent::KeyRelease); + } + sendKey(operationId, QEvent::KeyPress); + timer.start(); + repeatTimer.stop(); + break; + default: + break; + } + } + break; + default: + switch (buttonAction) { + case ERemConCoreApiButtonPress: + sendKey(operationId, QEvent::KeyPress); + repeatTimer.start(initialRepeatTime); + break; + case ERemConCoreApiButtonRelease: + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + case ERemConCoreApiButtonClick: + sendKey(operationId, QEvent::KeyPress); + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + default: + break; + } + break; + } + + if (handler) + handler->CompleteAnyKey(operationId); +} + +/* + * Sends volume up KeyRelease event + */ +void QS60KeyCapture::volumeUpClickTimerExpired() +{ + sendKey(ERemConCoreApiVolumeUp, QEvent::KeyRelease); +} + +/* + * Sends volume down KeyRelease event + */ +void QS60KeyCapture::volumeDownClickTimerExpired() +{ + sendKey(ERemConCoreApiVolumeDown, QEvent::KeyRelease); +} + +/* + * Repeats last key event + */ +void QS60KeyCapture::repeatTimerExpired() +{ + // set auto repeat flag on + repeatKeyEvent.iRepeats = 1; + coeEnv->SimulateKeyEventL(repeatKeyEvent, EEventKey); + repeatTimer.start(repeatTime); +} + +/* + * Maps RemCon operation id to key event (includes key code and scan codes) + */ +TKeyEvent QS60KeyCapture::mapToKeyEvent(TRemConCoreApiOperationId operationId) +{ + TKeyEvent keyEvent; + keyEvent.iModifiers = 0; + keyEvent.iRepeats = 0; + keyEvent.iCode = qt_keymapper_private()->mapS60RemConIdToS60Key(operationId); + keyEvent.iScanCode = qt_keymapper_private()->mapS60RemConIdToS60ScanCodes(operationId); + return keyEvent; +} + +/* + * Delivers key events to the application. + * RemCon operations are converted to simulated key events + */ +void QS60KeyCapture::sendKey(TRemConCoreApiOperationId operationId, QEvent::Type type) +{ + TKeyEvent keyEvent = mapToKeyEvent(operationId); + + if (type == QEvent::KeyPress) { + coeEnv->SimulateKeyEventL(keyEvent, EEventKeyDown); + coeEnv->SimulateKeyEventL(keyEvent, EEventKey); + // save the key press event for repeats + repeatKeyEvent = keyEvent; + } else { + coeEnv->SimulateKeyEventL(keyEvent, EEventKeyUp); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/s60framework/qs60keycapture_p.h b/src/gui/s60framework/qs60keycapture_p.h new file mode 100644 index 0000000..a50fad7 --- /dev/null +++ b/src/gui/s60framework/qs60keycapture_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Symbian application wrapper of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QS60KEYCAPTURE_P_H +#define QS60KEYCAPTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifdef Q_OS_SYMBIAN + +#include <QObject> +#include <QEvent> +#include <QTimer> +#include <remconcoreapitargetobserver.h> +#include <w32std.h> + +class CRemConInterfaceSelector; +class CRemConCoreApiTarget; +class CResponseHandler; +class CCoeEnv; + +QT_BEGIN_NAMESPACE + +class QS60KeyCapture: public QObject, + public MRemConCoreApiTargetObserver +{ + Q_OBJECT +public: + QS60KeyCapture(CCoeEnv *env, QObject *parent = 0); + ~QS60KeyCapture(); + + void MrccatoCommand(TRemConCoreApiOperationId operationId, + TRemConCoreApiButtonAction buttonAct); + +private slots: + void volumeUpClickTimerExpired(); + void volumeDownClickTimerExpired(); + void repeatTimerExpired(); + +private: + void sendKey(TRemConCoreApiOperationId operationId, QEvent::Type type); + TKeyEvent mapToKeyEvent(TRemConCoreApiOperationId operationId); + void initRemCon(); + +private: + QS60KeyCapture(); + Q_DISABLE_COPY(QS60KeyCapture) + + CCoeEnv *coeEnv; + + CRemConInterfaceSelector *selector; + CRemConCoreApiTarget *target; + CResponseHandler *handler; + + QTimer volumeUpClickTimer; + QTimer volumeDownClickTimer; + + TKeyEvent repeatKeyEvent; + int initialRepeatTime; // time before first repeat key event + int repeatTime; // time between subsequent repeat key events + QTimer repeatTimer; +}; + +QT_END_NAMESPACE + +#endif // Q_OS_SYMBIAN +#endif // QS60KEYCAPTURE_P_H diff --git a/src/gui/s60framework/qs60mainappui.cpp b/src/gui/s60framework/qs60mainappui.cpp index ec97f7b..bd3017c 100644 --- a/src/gui/s60framework/qs60mainappui.cpp +++ b/src/gui/s60framework/qs60mainappui.cpp @@ -59,6 +59,7 @@ #include <private/qmenu_p.h> #include <private/qt_s60_p.h> #include <qdebug.h> +#include "qs60keycapture_p.h" //Animated wallpapers in Qt applications are not supported. const TInt KAknDisableAnimationBackground = 0x02000000; @@ -66,6 +67,20 @@ const TInt KAknSingleClickCompatible = 0x01000000; QT_BEGIN_NAMESPACE +static QS60KeyCapture *qt_S60KeyCapture = 0; + +static void installS60KeyCapture(CCoeEnv *env) +{ + if (QApplication::testAttribute(Qt::AA_CaptureMultimediaKeys)) + qt_S60KeyCapture = new QS60KeyCapture(env); +} + +static void removeS60KeyCapture() +{ + delete qt_S60KeyCapture; + qt_S60KeyCapture = 0; +} + /*! \class QS60MainAppUi \since 4.6 @@ -127,6 +142,7 @@ void QS60MainAppUi::ConstructL() } #endif BaseConstructL(flags); + installS60KeyCapture(iCoeEnv); } /*! @@ -142,6 +158,7 @@ QS60MainAppUi::QS60MainAppUi() */ QS60MainAppUi::~QS60MainAppUi() { + removeS60KeyCapture(); } /*! diff --git a/src/gui/s60framework/s60framework.pri b/src/gui/s60framework/s60framework.pri index 19525b7..e30d2c0 100644 --- a/src/gui/s60framework/s60framework.pri +++ b/src/gui/s60framework/s60framework.pri @@ -1,10 +1,13 @@ SOURCES += s60framework/qs60mainapplication.cpp \ - s60framework/qs60mainappui.cpp \ - s60framework/qs60maindocument.cpp - -HEADERS += s60framework/qs60mainapplication_p.h \ - s60framework/qs60mainapplication.h \ - s60framework/qs60mainappui.h \ - s60framework/qs60maindocument.h - -!isEmpty(QT_LIBINFIX): DEFINES += QT_LIBINFIX_UNQUOTED=$$QT_LIBINFIX
\ No newline at end of file + s60framework/qs60mainappui.cpp \ + s60framework/qs60maindocument.cpp \ + s60framework/qs60keycapture.cpp +HEADERS += qs60keycapture_p.h \ + s60framework/qs60mainapplication_p.h \ + s60framework/qs60mainapplication.h \ + s60framework/qs60mainappui.h \ + s60framework/qs60maindocument.h \ + s60framework/qs60keycapture_p.h +LIBS += -lremconcoreapi \ + -lremconinterfacebase +!isEmpty(QT_LIBINFIX):DEFINES += QT_LIBINFIX_UNQUOTED=$$QT_LIBINFIX diff --git a/src/plugins/bearer/symbian/symbianengine.cpp b/src/plugins/bearer/symbian/symbianengine.cpp index 38ae0c8..fc2791a 100644 --- a/src/plugins/bearer/symbian/symbianengine.cpp +++ b/src/plugins/bearer/symbian/symbianengine.cpp @@ -258,7 +258,7 @@ void SymbianEngine::updateConfigurationsL() #ifdef SNAP_FUNCTIONALITY_AVAILABLE // S60 version is >= Series60 3rd Edition Feature Pack 2 TInt error = KErrNone; - + // Loop through all IAPs RArray<TUint32> connectionMethods; // IAPs CleanupClosePushL(connectionMethods); @@ -292,7 +292,7 @@ void SymbianEngine::updateConfigurationsL() CleanupStack::PopAndDestroy(&connectionMethod); } CleanupStack::PopAndDestroy(&connectionMethods); - + // Loop through all SNAPs RArray<TUint32> destinations; CleanupClosePushL(destinations); @@ -462,7 +462,7 @@ void SymbianEngine::updateConfigurationsL() break; } } - } + } } foreach (const QString &oldIface, knownSnapConfigs) { @@ -492,13 +492,13 @@ SymbianNetworkConfigurationPrivate *SymbianEngine::configFromConnectionMethodL( SymbianNetworkConfigurationPrivate *cpPriv = new SymbianNetworkConfigurationPrivate; TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)); - + HBufC *pName = connectionMethod.GetStringAttributeL(CMManager::ECmName); CleanupStack::PushL(pName); QT_TRYCATCH_LEAVING(cpPriv->name = QString::fromUtf16(pName->Ptr(),pName->Length())); CleanupStack::PopAndDestroy(pName); pName = NULL; - + TUint32 bearerId = connectionMethod.GetIntAttributeL(CMManager::ECmCommsDBBearerType); switch (bearerId) { case KCommDbBearerCSD: @@ -523,7 +523,7 @@ SymbianNetworkConfigurationPrivate *SymbianEngine::configFromConnectionMethodL( cpPriv->bearerType = QNetworkConfiguration::BearerUnknown; break; } - + TInt error = KErrNone; TUint32 bearerType = connectionMethod.GetIntAttributeL(CMManager::ECmBearerType); switch (bearerType) { @@ -547,13 +547,13 @@ SymbianNetworkConfigurationPrivate *SymbianEngine::configFromConnectionMethodL( CleanupStack::PopAndDestroy(pName); pName = NULL; } - + cpPriv->state = QNetworkConfiguration::Defined; TBool isConnected = connectionMethod.GetBoolAttributeL(CMManager::ECmConnected); if (isConnected) { cpPriv->state = QNetworkConfiguration::Active; } - + cpPriv->isValid = true; cpPriv->id = ident; cpPriv->numericId = iapId; @@ -569,7 +569,7 @@ bool SymbianEngine::readNetworkConfigurationValuesFromCommsDb( { TRAPD(error, readNetworkConfigurationValuesFromCommsDbL(aApId,apNetworkConfiguration)); if (error != KErrNone) { - return false; + return false; } return true; } @@ -577,22 +577,22 @@ bool SymbianEngine::readNetworkConfigurationValuesFromCommsDb( void SymbianEngine::readNetworkConfigurationValuesFromCommsDbL( TUint32 aApId, SymbianNetworkConfigurationPrivate *apNetworkConfiguration) { - CApDataHandler* pDataHandler = CApDataHandler::NewLC(*ipCommsDB); - CApAccessPointItem* pAPItem = CApAccessPointItem::NewLC(); + CApDataHandler* pDataHandler = CApDataHandler::NewLC(*ipCommsDB); + CApAccessPointItem* pAPItem = CApAccessPointItem::NewLC(); TBuf<KCommsDbSvrMaxColumnNameLength> name; - + CApUtils* pApUtils = CApUtils::NewLC(*ipCommsDB); TUint32 apId = pApUtils->WapIdFromIapIdL(aApId); - + pDataHandler->AccessPointDataL(apId,*pAPItem); pAPItem->ReadTextL(EApIapName, name); if (name.Compare(_L("Easy WLAN")) == 0) { // "Easy WLAN" won't be accepted to the Configurations list User::Leave(KErrNotFound); } - + QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(aApId)); - + QT_TRYCATCH_LEAVING(apNetworkConfiguration->name = QString::fromUtf16(name.Ptr(),name.Length())); apNetworkConfiguration->isValid = true; apNetworkConfiguration->id = ident; @@ -603,7 +603,7 @@ void SymbianEngine::readNetworkConfigurationValuesFromCommsDbL( apNetworkConfiguration->purpose = QNetworkConfiguration::UnknownPurpose; apNetworkConfiguration->roamingSupported = false; switch (pAPItem->BearerTypeL()) { - case EApBearerTypeCSD: + case EApBearerTypeCSD: apNetworkConfiguration->bearerType = QNetworkConfiguration::Bearer2G; break; case EApBearerTypeGPRS: @@ -628,7 +628,7 @@ void SymbianEngine::readNetworkConfigurationValuesFromCommsDbL( apNetworkConfiguration->bearerType = QNetworkConfiguration::BearerUnknown; break; } - + CleanupStack::PopAndDestroy(pApUtils); CleanupStack::PopAndDestroy(pAPItem); CleanupStack::PopAndDestroy(pDataHandler); @@ -667,7 +667,7 @@ QNetworkConfigurationPrivatePointer SymbianEngine::defaultConfigurationL() ptr = accessPointConfigurations.value(iface); } #endif - + if (ptr) { QMutexLocker configLocker(&ptr->mutex); if (ptr->isValid) @@ -687,7 +687,7 @@ void SymbianEngine::updateActiveAccessPoints() TUint connectionCount; iConnectionMonitor.GetConnectionCount(connectionCount, status); User::WaitForRequest(status); - + // Go through all connections and set state of related IAPs to Active. // Status needs to be checked carefully, because ConnMon lists also e.g. // WLAN connections that are being currently tried --> we don't want to @@ -766,7 +766,7 @@ void SymbianEngine::accessPointScanningReady(TBool scanSuccessful, TConnMonIapIn iUpdateGoingOn = false; if (scanSuccessful) { QList<QString> unavailableConfigs = accessPointConfigurations.keys(); - + // Set state of returned IAPs to Discovered // if state is not already Active for(TUint i=0; i<iapInfo.iCount; i++) { @@ -783,7 +783,7 @@ void SymbianEngine::accessPointScanningReady(TBool scanSuccessful, TConnMonIapIn } } } - + // Make sure that state of rest of the IAPs won't be Active foreach (const QString &iface, unavailableConfigs) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); @@ -795,7 +795,7 @@ void SymbianEngine::accessPointScanningReady(TBool scanSuccessful, TConnMonIapIn } updateStatesToSnaps(); - + if (!iFirstUpdate) { startCommsDatabaseNotifications(); mutex.unlock(); @@ -847,7 +847,7 @@ void SymbianEngine::updateStatesToSnaps() } else { changeConfigurationStateTo(ptr, QNetworkConfiguration::Defined); } - } + } } #ifdef SNAP_FUNCTIONALITY_AVAILABLE @@ -1065,7 +1065,7 @@ void SymbianEngine::EventL(const CConnMonEventBase& aEvent) if (connectionStatus == KConfigDaemonStartingRegistration) { TUint connectionId = realEvent->ConnectionId(); TUint subConnectionCount = 0; - TUint apId; + TUint apId; TRequestStatus status; iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); @@ -1091,7 +1091,7 @@ void SymbianEngine::EventL(const CConnMonEventBase& aEvent) // Connection has been successfully opened TUint connectionId = realEvent->ConnectionId(); TUint subConnectionCount = 0; - TUint apId; + TUint apId; TRequestStatus status; iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); @@ -1147,7 +1147,7 @@ void SymbianEngine::EventL(const CConnMonEventBase& aEvent) connectionId, QNetworkSession::Disconnected); ); } - + bool online = false; foreach (const QString &iface, accessPointConfigurations.keys()) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); @@ -1163,7 +1163,7 @@ void SymbianEngine::EventL(const CConnMonEventBase& aEvent) } } } - break; + break; case EConnMonIapAvailabilityChange: { @@ -1177,7 +1177,7 @@ void SymbianEngine::EventL(const CConnMonEventBase& aEvent) QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); if (ptr) { - // Configuration is either Discovered or Active + // Configuration is either Discovered or Active QT_TRYCATCH_LEAVING(changeConfigurationStateAtMinTo(ptr, QNetworkConfiguration::Discovered)); unDiscoveredConfigs.removeOne(ident); } @@ -1363,7 +1363,7 @@ AccessPointsAvailabilityScanner::AccessPointsAvailabilityScanner(SymbianEngine& RConnectionMonitor& connectionMonitor) : CActive(CActive::EPriorityHigh), iOwner(owner), iConnectionMonitor(connectionMonitor) { - CActiveScheduler::Add(this); + CActiveScheduler::Add(this); } AccessPointsAvailabilityScanner::~AccessPointsAvailabilityScanner() diff --git a/src/plugins/bearer/symbian/symbianengine.h b/src/plugins/bearer/symbian/symbianengine.h index 7455d25..a205c97 100644 --- a/src/plugins/bearer/symbian/symbianengine.h +++ b/src/plugins/bearer/symbian/symbianengine.h @@ -145,10 +145,10 @@ public: Q_SIGNALS: void onlineStateChanged(bool isOnline); - + void configurationStateChanged(quint32 accessPointId, quint32 connMonId, QNetworkSession::State newState); - + public Q_SLOTS: void updateConfigurations(); void delayedConfigurationUpdate(); @@ -169,8 +169,8 @@ private: TUint32 aApId, SymbianNetworkConfigurationPrivate *apNetworkConfiguration); void readNetworkConfigurationValuesFromCommsDbL( TUint32 aApId, SymbianNetworkConfigurationPrivate *apNetworkConfiguration); -#endif - +#endif + void updateConfigurationsL(); void updateActiveAccessPoints(); void updateAvailableAccessPoints(); @@ -190,7 +190,7 @@ protected: // From CActive void RunL(); void DoCancel(); - + private: // MConnectionMonitorObserver void EventL(const CConnMonEventBase& aEvent); @@ -200,7 +200,7 @@ private: #endif private: // Data - bool iFirstUpdate; + bool iFirstUpdate; CCommsDatabase* ipCommsDB; RConnectionMonitor iConnectionMonitor; @@ -227,11 +227,11 @@ class AccessPointsAvailabilityScanner : public CActive { public: AccessPointsAvailabilityScanner(SymbianEngine& owner, - RConnectionMonitor& connectionMonitor); + RConnectionMonitor& connectionMonitor); ~AccessPointsAvailabilityScanner(); void StartScanning(); - + protected: // From CActive void RunL(); void DoCancel(); diff --git a/tests/auto/qs60mainapplication/qs60mainapplication.pro b/tests/auto/qs60mainapplication/qs60mainapplication.pro index bbd6c30..de3f59d 100644 --- a/tests/auto/qs60mainapplication/qs60mainapplication.pro +++ b/tests/auto/qs60mainapplication/qs60mainapplication.pro @@ -2,3 +2,4 @@ load(qttest_p4) SOURCES += tst_qs60mainapplication.cpp symbian:LIBS += -lapparc -leikcore -lcone -lavkon +symbian:LIBS += -lremconcoreapi -lremconinterfacebase
\ No newline at end of file diff --git a/tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp b/tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp index 069fd14..967ce4e 100644 --- a/tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp +++ b/tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp @@ -59,6 +59,8 @@ public slots: void cleanup(); private slots: void customQS60MainApplication(); + void testMultimediaKeys_data(); + void testMultimediaKeys(); }; void tst_QS60MainApplication::initTestCase() @@ -115,6 +117,201 @@ CApaApplication *factory() { return new (ELeave) CustomMainApplication; } + +#include <remconcoreapicontrollerobserver.h> +#include <remconcoreapicontroller.h> +#include <remconinterfaceselector.h> +#include <QTimer> +#include <QSignalSpy> + +class KeyGenerator : public QObject, + public MRemConCoreApiControllerObserver +{ + Q_OBJECT +public: + KeyGenerator(QObject *parent = 0); + ~KeyGenerator(); + void MrccacoResponse(TRemConCoreApiOperationId operationId, TInt error); + + void simulateKey(int qtKey); + +private: + void init(); + void cleanup(); + + CRemConInterfaceSelector *interfaceSelector; + CRemConCoreApiController *coreController; +}; + +KeyGenerator::KeyGenerator(QObject *parent) : QObject(parent) +{ + init(); +} + +KeyGenerator::~KeyGenerator() +{ + cleanup(); +} + +void KeyGenerator::MrccacoResponse(TRemConCoreApiOperationId operationId, TInt error) +{ + Q_UNUSED(operationId); + Q_UNUSED(error); +} + +/* + * Generates keyPress and keyRelease events for given key + */ +void KeyGenerator::simulateKey(int qtKey) +{ + if (!coreController) + return; + + TRemConCoreApiButtonAction action = ERemConCoreApiButtonClick; + TUint numRemotes = 0; + TRequestStatus status; + bool wait = true; + + switch (qtKey) { + // media keys + case Qt::Key_VolumeUp: + coreController->VolumeUp(status, numRemotes, action); + break; + case Qt::Key_VolumeDown: + coreController->VolumeDown(status, numRemotes, action); + break; + case Qt::Key_MediaStop: + coreController->Stop(status, numRemotes, action); + break; + case Qt::Key_MediaTogglePlayPause: + coreController->PausePlayFunction(status, numRemotes, action); + break; + case Qt::Key_MediaNext: + coreController->Forward(status, numRemotes, action); + break; + case Qt::Key_MediaPrevious: + coreController->Backward(status, numRemotes, action); + break; + case Qt::Key_AudioForward: + coreController->FastForward(status, numRemotes, action); + break; + case Qt::Key_AudioRewind: + coreController->Rewind(status, numRemotes, action); + break; + // accessory keys + case Qt::Key_Select: + coreController->Select(status, numRemotes, action); + break; + case Qt::Key_Enter: + coreController->Enter(status, numRemotes, action); + break; + case Qt::Key_PageUp: + coreController->PageUp(status, numRemotes, action); + break; + case Qt::Key_PageDown: + coreController->PageDown(status, numRemotes, action); + break; + case Qt::Key_Left: + coreController->Left(status, numRemotes, action); + break; + case Qt::Key_Right: + coreController->Right(status, numRemotes, action); + break; + case Qt::Key_Up: + coreController->Up(status, numRemotes, action); + break; + case Qt::Key_Down: + coreController->Down(status, numRemotes, action); + break; + case Qt::Key_Help: + coreController->Help(status, numRemotes, action); + break; + case Qt::Key_F1: + coreController->F1(status, numRemotes, action); + break; + case Qt::Key_F2: + coreController->F2(status, numRemotes, action); + break; + case Qt::Key_F3: + coreController->F3(status, numRemotes, action); + break; + case Qt::Key_F4: + coreController->F4(status, numRemotes, action); + break; + case Qt::Key_F5: + coreController->F5(status, numRemotes, action); + break; + default: + wait = false; + break; + } + + if (wait) + User::WaitForRequest(status); +} + +void KeyGenerator::init() +{ + try { + QT_TRAP_THROWING(interfaceSelector = CRemConInterfaceSelector::NewL()); + QT_TRAP_THROWING(coreController = CRemConCoreApiController::NewL(*interfaceSelector, *this)); + QT_TRAP_THROWING(interfaceSelector->OpenControllerL()); + } catch (const std::exception &e) { + cleanup(); + } +} + +void KeyGenerator::cleanup() +{ + delete interfaceSelector; + interfaceSelector = 0; + coreController = 0; +} + +const int keyEventTimeout = 2000; // 2secs + +class TestWidget : public QWidget +{ + Q_OBJECT +public: + TestWidget(QWidget *parent = 0); + ~TestWidget(); + +signals: + void keyPress(int key); + void keyRelease(int key); + +protected: + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + +private: + QTimer exitTimer; +}; + +TestWidget::TestWidget(QWidget *parent) : QWidget(parent) +{ + // quit if no events are received + exitTimer.setSingleShot(true); + exitTimer.start(keyEventTimeout); + connect(&exitTimer, SIGNAL(timeout()), qApp, SLOT(quit())); +} + +TestWidget::~TestWidget() +{ +} + +void TestWidget::keyPressEvent(QKeyEvent *event) +{ + emit keyPress(event->key()); +} + +void TestWidget::keyReleaseEvent(QKeyEvent *event) +{ + emit keyRelease(event->key()); + qApp->quit(); // test is done so quit immediately +} + #endif // Q_WS_S60 void tst_QS60MainApplication::customQS60MainApplication() @@ -129,5 +326,67 @@ void tst_QS60MainApplication::customQS60MainApplication() #endif } +void tst_QS60MainApplication::testMultimediaKeys_data() +{ + QTest::addColumn<int>("key"); + + QTest::newRow("Key_VolumeUp") << (int)Qt::Key_VolumeUp; + QTest::newRow("Key_VolumeDown") << (int)Qt::Key_VolumeDown; + QTest::newRow("Key_MediaStop") << (int)Qt::Key_MediaStop; + QTest::newRow("Key_MediaTogglePlayPause") << (int)Qt::Key_MediaTogglePlayPause; + QTest::newRow("Key_MediaNext") << (int)Qt::Key_MediaNext; + QTest::newRow("Key_MediaPrevious") << (int)Qt::Key_MediaPrevious; + QTest::newRow("Key_AudioForward") << (int)Qt::Key_AudioForward; + QTest::newRow("Key_AudioRewind") << (int)Qt::Key_AudioRewind; + + QTest::newRow("Key_Select") << (int)Qt::Key_Select; + QTest::newRow("Key_Enter") << (int)Qt::Key_Enter; + QTest::newRow("Key_PageUp") << (int)Qt::Key_PageUp; + QTest::newRow("Key_PageDown") << (int)Qt::Key_PageDown; + QTest::newRow("Key_Left") << (int)Qt::Key_Left; + QTest::newRow("Key_Right") << (int)Qt::Key_Right; + QTest::newRow("Key_Up") << (int)Qt::Key_Up; + QTest::newRow("Key_Down") << (int)Qt::Key_Down; + QTest::newRow("Key_Help") << (int)Qt::Key_Help; + QTest::newRow("Key_F1") << (int)Qt::Key_F1; + QTest::newRow("Key_F2") << (int)Qt::Key_F2; + QTest::newRow("Key_F3") << (int)Qt::Key_F3; + QTest::newRow("Key_F4") << (int)Qt::Key_F4; + QTest::newRow("Key_F5") << (int)Qt::Key_F5; +} + +void tst_QS60MainApplication::testMultimediaKeys() +{ +#ifndef Q_WS_S60 + QSKIP("This is an S60-only test", SkipAll); +#elif __WINS__ + QSKIP("S60 emulator not supported", SkipAll); +#else + QApplication::setAttribute(Qt::AA_CaptureMultimediaKeys); + int argc = 1; + char *argv = "tst_qs60mainapplication"; + QApplication app(argc, &argv); + + QFETCH(int, key); + KeyGenerator keyGen; + keyGen.simulateKey(key); + + TestWidget widget; + QSignalSpy keyPressSpy(&widget, SIGNAL(keyPress(int))); + QSignalSpy keyReleaseSpy(&widget, SIGNAL(keyRelease(int))); + + widget.show(); + app.exec(); + + QCOMPARE(keyPressSpy.count(), 1); + QList<QVariant> arguments = keyPressSpy.takeFirst(); + QVERIFY(arguments.at(0).toInt() == key); + + QCOMPARE(keyReleaseSpy.count(), 1); + arguments = keyReleaseSpy.takeFirst(); + QVERIFY(arguments.at(0).toInt() == key); +#endif +} + QTEST_APPLESS_MAIN(tst_QS60MainApplication) #include "tst_qs60mainapplication.moc" |