summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qnamespace.h1
-rw-r--r--src/corelib/global/qnamespace.qdoc9
-rw-r--r--src/gui/kernel/qkeymapper_p.h2
-rw-r--r--src/gui/kernel/qkeymapper_s60.cpp195
-rw-r--r--src/gui/s60framework/qs60keycapture.cpp308
-rw-r--r--src/gui/s60framework/qs60keycapture_p.h116
-rw-r--r--src/gui/s60framework/qs60mainappui.cpp17
-rw-r--r--src/gui/s60framework/s60framework.pri21
-rw-r--r--tests/auto/qs60mainapplication/qs60mainapplication.pro1
-rw-r--r--tests/auto/qs60mainapplication/tst_qs60mainapplication.cpp259
10 files changed, 838 insertions, 91 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 99479d0b63..e24753338c 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 9f59c6e8e9..7d3b7f617f 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -167,6 +167,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 2607c2a1a9..1d624f9d3c 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 1113b77875..2a7083e821 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 0000000000..db4cc0e89c
--- /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 QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 0000000000..41cb7657c0
--- /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 QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 bfd18256e1..f120287012 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 19525b7fdb..e30d2c0ff5 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/tests/auto/qs60mainapplication/qs60mainapplication.pro b/tests/auto/qs60mainapplication/qs60mainapplication.pro
index bbd6c3073a..de3f59d5d5 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 069fd14d58..967ce4ecd0 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"