diff options
author | axis <qt-info@nokia.com> | 2010-07-29 08:25:40 (GMT) |
---|---|---|
committer | axis <qt-info@nokia.com> | 2010-07-29 14:06:05 (GMT) |
commit | 041570ca54824ee358593f112b38e26632c3ebbf (patch) | |
tree | 2e000b7b5fc34d9ecd474fbd8b6d6b6fe5da7903 /src/gui | |
parent | 5a3246b198ced3634bd974919e78310d536be27e (diff) | |
download | Qt-041570ca54824ee358593f112b38e26632c3ebbf.zip Qt-041570ca54824ee358593f112b38e26632c3ebbf.tar.gz Qt-041570ca54824ee358593f112b38e26632c3ebbf.tar.bz2 |
Fixed key event handling on Symbian.
After looking at QTBUG-11338 and Qt's behavior, it became clear that
we needed more state tracking in the key event handling code, to be
able to handle all the different ways that we can get key events.
This inspired having three states for each used scan code, instead of
the old code, which only took the current scan code into account.
This should make Symbian behave identically to Linux.
Task: QTBUG-11338
AutoTest: N/A, platform specific code. There was lots of manual
testing on 5800, N97 and N95.
RevBy: Jason Barron
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/kernel/qapplication_s60.cpp | 164 | ||||
-rw-r--r-- | src/gui/kernel/qt_s60_p.h | 9 |
2 files changed, 118 insertions, 55 deletions
diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index bb30bcb..f8734b2 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -642,71 +642,125 @@ TKeyResponse QSymbianControl::OfferKeyEventL(const TKeyEvent& keyEvent, TEventCo TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCode type) { - switch (type) { - //case EEventKeyDown: // <-- Intentionally left out. See below. - case EEventKeyUp: - case EEventKey: - { + /* + S60 has a confusing way of delivering key events. There are three types of + events: EEventKey, EEventKeyDown and EEventKeyUp. When a key is pressed, + EEventKeyDown is first generated, followed by EEventKey. Then, when the key is + released, EEventKeyUp is generated. + However, it is possible that only the EEventKey is generated alone, typically + in relation to virtual keyboards. In that case we need to take care to + generate both press and release events in Qt, since applications expect that. + We do this by having three states for each used scan code, depending on the + events received. See the switch below for what happens in each state + transition. + */ + + if (type != EEventKeyDown) if (handleVirtualMouse(keyEvent, type) == EKeyWasConsumed) return EKeyWasConsumed; - // S60 has a confusing way of delivering key events. There are three types of - // events: EKeyEvent, EKeyEventDown and EKeyEventUp. When a key is pressed, the - // two first events are generated. When releasing the key, the last one is - // generated. - // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp events, - // we need to do some special tricks to map it to the Qt way. First, we completely - // discard EKeyEventDown events, since they are redundant. Second, since - // EKeyEventUp does not give us a keysym, we need to cache the keysyms from - // the EKeyEvent events. This is what resolveS60ScanCode does. - - - - TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode, - keyEvent.iCode); - int keyCode; - if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used - keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode); - } else if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) { - // Normal characters keys. - keyCode = s60Keysym; - } else { - // Special S60 keys. - keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym); + TKeyResponse ret = EKeyWasNotConsumed; +#define GET_RETURN(x) (ret = ((x) == EKeyWasConsumed) ? EKeyWasConsumed : ret) + + // This top level switch corresponds to the states, and the inner switches + // correspond to the transitions. + QS60Data::ScanCodeState &scanCodeState = S60->scanCodeStates[keyEvent.iScanCode]; + switch (scanCodeState) { + case QS60Data::Unpressed: + switch (type) { + case EEventKeyDown: + scanCodeState = QS60Data::KeyDown; + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + break; + case EEventKeyUp: + // No action. + break; } - - Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers); - QKeyEventEx qKeyEvent(type == EEventKeyUp ? QEvent::KeyRelease : QEvent::KeyPress, keyCode, - mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods), - (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers); - QWidget *widget; - widget = QWidget::keyboardGrabber(); - if (!widget) { - if (QApplicationPrivate::popupWidgets != 0) { - widget = QApplication::activePopupWidget()->focusWidget(); - if (!widget) { - widget = QApplication::activePopupWidget(); - } - } else { - widget = QApplicationPrivate::focus_widget; - if (!widget) { - widget = qwidget; - } - } + break; + case QS60Data::KeyDown: + switch (type) { + case EEventKeyDown: + // This should never happen, just stay in this state to be safe. + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + scanCodeState = QS60Data::KeyDownAndKey; + break; + case EEventKeyUp: + scanCodeState = QS60Data::Unpressed; + break; + } + break; + case QS60Data::KeyDownAndKey: + switch (type) { + case EEventKeyDown: + // This should never happen, just stay in this state to be safe. + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + break; + case EEventKeyUp: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + scanCodeState = QS60Data::Unpressed; + break; } + break; + } + return ret; - QEventDispatcherS60 *dispatcher; - // It is theoretically possible for someone to install a different event dispatcher. - if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) { - if (dispatcher->excludeUserInputEvents()) { - dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent)); - return EKeyWasConsumed; +#undef GET_RETURN +} + +TKeyResponse QSymbianControl::sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type) +{ + // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp + // events, we need to cache the keysyms from the EKeyEvent events. This is what + // resolveS60ScanCode does. + TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode, + keyEvent.iCode); + int keyCode; + if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used + keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode); + } else if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) { + // Normal characters keys. + keyCode = s60Keysym; + } else { + // Special S60 keys. + keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym); + } + + Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers); + QKeyEventEx qKeyEvent(type, keyCode, mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods), + (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers); + QWidget *widget; + widget = QWidget::keyboardGrabber(); + if (!widget) { + if (QApplicationPrivate::popupWidgets != 0) { + widget = QApplication::activePopupWidget()->focusWidget(); + if (!widget) { + widget = QApplication::activePopupWidget(); + } + } else { + widget = QApplicationPrivate::focus_widget; + if (!widget) { + widget = qwidget; } } - return sendKeyEvent(widget, &qKeyEvent); } + + QEventDispatcherS60 *dispatcher; + // It is theoretically possible for someone to install a different event dispatcher. + if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) { + if (dispatcher->excludeUserInputEvents()) { + dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent)); + return EKeyWasConsumed; + } } - return EKeyWasNotConsumed; + return sendKeyEvent(widget, &qKeyEvent); } TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type) diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index 6004961..7f0c99e 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -143,6 +143,14 @@ public: int menuBeingConstructed : 1; int memoryLimitForHwRendering; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type + + enum ScanCodeState { + Unpressed, + KeyDown, + KeyDownAndKey + }; + QHash<TInt, ScanCodeState> scanCodeStates; + static inline void updateScreenSize(); inline RWsSession& wsSession(); static inline RWindowGroup& windowGroup(); @@ -224,6 +232,7 @@ protected: private: void HandlePointerEvent(const TPointerEvent& aPointerEvent); TKeyResponse OfferKeyEvent(const TKeyEvent& aKeyEvent,TEventCode aType); + TKeyResponse sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type); TKeyResponse sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent); TKeyResponse handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type); bool sendMouseEvent(QWidget *widget, QMouseEvent *mEvent); |