From 71df6edab122730c38ac238e168a4cc35b6f4857 Mon Sep 17 00:00:00 2001 From: mread Date: Tue, 6 Apr 2010 14:05:01 +0100 Subject: QTBUG-4887 and other exception safety fixes This change includes a fix for QTBUG-4887 and other exception safety problems found while testing it. The QTBUG-4887 fix is to qimage.cpp. QImage doesn't throw exceptions on failure like a proper class should, instead it tries to fail "nice". What happens here is that setAlphaChannel would crash on OOM as after the convertToFormat call, d could be NULL. This new version checks the result of the conversion before using it. The other fixes are all cases where exceptions were thrown from destructors. I added code to the test app to help debug these cases, and I fixed all the problems I found. With these changes, tst_exceptionsafety_objects runs and passes on the Symbian emulator. Reviewed-by: Shane Kearns --- src/gui/image/qimage.cpp | 6 +++++- src/gui/kernel/qapplication_s60.cpp | 9 +++++++-- src/gui/kernel/qwidget.cpp | 8 ++++++-- src/gui/widgets/qmenu.cpp | 14 ++++++++------ .../tst_exceptionsafety_objects.cpp | 19 +++++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 4f5efa1..5d2b4c0 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -5667,7 +5667,11 @@ void QImage::setAlphaChannel(const QImage &alphaChannel) detach(); - *this = convertToFormat(QImage::Format_ARGB32_Premultiplied); + QImage converted = convertToFormat(QImage::Format_ARGB32_Premultiplied); + if (!converted.isNull()) + *this = converted; + else + return; // Slight optimization since alphachannels are returned as 8-bit grays. if (alphaChannel.d->depth == 8 && alphaChannel.isGrayscale()) { diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 37d1b62..e986ce9 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -372,8 +372,13 @@ QSymbianControl::~QSymbianControl() { if (S60->curWin == this) S60->curWin = 0; - if (!QApplicationPrivate::is_app_closing) - setFocusSafely(false); + if (!QApplicationPrivate::is_app_closing) { + QT_TRY { + setFocusSafely(false); + } QT_CATCH(const std::exception&) { + // ignore exceptions, nothing can be done + } + } S60->appUi()->RemoveFromStack(this); delete m_longTapDetector; } diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index cd943cd..e180001 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -1487,8 +1487,12 @@ QWidget::~QWidget() if (QWidgetPrivate::allWidgets) // might have been deleted by ~QApplication QWidgetPrivate::allWidgets->remove(this); - QEvent e(QEvent::Destroy); - QCoreApplication::sendEvent(this, &e); + QT_TRY { + QEvent e(QEvent::Destroy); + QCoreApplication::sendEvent(this, &e); + } QT_CATCH(const std::exception&) { + // if this fails we can't do anything about it but at least we are not allowed to throw. + } } int QWidgetPrivate::instanceCounter = 0; // Current number of widget instances diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index a9978f9..c7573bf 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -1406,12 +1406,14 @@ QMenu::QMenu(QMenuPrivate &dd, QWidget *parent) QMenu::~QMenu() { Q_D(QMenu); - QHash::iterator it = d->widgetItems.begin(); - for (; it != d->widgetItems.end(); ++it) { - if (QWidget *widget = it.value()) { - QWidgetAction *action = static_cast(it.key()); - action->releaseWidget(widget); - *it = 0; + if (!d->widgetItems.isEmpty()) { // avoid detach on shared null hash + QHash::iterator it = d->widgetItems.begin(); + for (; it != d->widgetItems.end(); ++it) { + if (QWidget *widget = it.value()) { + QWidgetAction *action = static_cast(it.key()); + action->releaseWidget(widget); + *it = 0; + } } } diff --git a/tests/auto/exceptionsafety_objects/tst_exceptionsafety_objects.cpp b/tests/auto/exceptionsafety_objects/tst_exceptionsafety_objects.cpp index 4433c7a..91d1a44 100644 --- a/tests/auto/exceptionsafety_objects/tst_exceptionsafety_objects.cpp +++ b/tests/auto/exceptionsafety_objects/tst_exceptionsafety_objects.cpp @@ -43,6 +43,7 @@ #include #include +#include QT_USE_NAMESPACE @@ -285,8 +286,26 @@ void tst_ExceptionSafetyObjects::safeMessageHandler(QtMsgType type, const char * allocFailer.reactivateAt(currentIndex); } +typedef void (*PVF)(); +PVF defaultTerminate; +void debugTerminate() +{ + // you can detect uncaught exceptions with a breakpoint in here + (*defaultTerminate)(); +} + +PVF defaultUnexpected; +void debugUnexpected() +{ + // you can detect unexpected exceptions with a breakpoint in here + (*defaultUnexpected)(); +} + void tst_ExceptionSafetyObjects::initTestCase() { + // set handlers for bad exception cases, you might want to step in and breakpoint the default handlers too + defaultTerminate = std::set_terminate(&debugTerminate); + defaultUnexpected = std::set_unexpected(&debugUnexpected); testMessageHandler = qInstallMsgHandler(safeMessageHandler); QVERIFY(AllocFailer::initialize()); -- cgit v0.12 From 5ae2cbe99d06e1f1d037cd9a7868f2e1fd3f4c4c Mon Sep 17 00:00:00 2001 From: Janne Anttila Date: Thu, 8 Apr 2010 08:29:21 +0300 Subject: Fixed focus and window activation events on Symbian when opening menu. As described in QTBUG-8698, Qt for Symbian has been generating incorrect focus and window activation events. This has happened since launching menu from QSoftkeyManager with TryDisplayMenuBarL, invokes eventually QSymbianControl::FocusChanged. But when the FocusChanged is called menu being launched is not yet set to visible, meaning that IsDisplayingMenuOrDialog returns false. Because there is no way in platform to detect that menu is being launhced, the fix is to add a new flag QS60Data, which can be used to detect if FocusChanged event is received due to the fact that menu is being constructed/launched. Task-number: QTBUG-8698 * Fixes issues 2, 3 and 4 Reviewed-by: Sami Merila --- src/gui/kernel/qapplication_s60.cpp | 3 ++- src/gui/kernel/qsoftkeymanager_s60.cpp | 19 ++++++++++++++++--- src/gui/kernel/qsoftkeymanager_s60_p.h | 1 + src/gui/kernel/qt_s60_p.h | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index e986ce9..61beb4f 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -994,7 +994,7 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) } #endif } else if (QApplication::activeWindow() == qwidget->window()) { - if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog()) { + if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { QWidget *fw = QApplication::focusWidget(); if (fw) { QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); @@ -1244,6 +1244,7 @@ void qt_init(QApplicationPrivate * /* priv */, int) } S60->avkonComponentsSupportTransparency = false; + S60->menuBeingConstructed = false; #ifdef Q_WS_S60 TUid KCRUidAvkon = { 0x101F876E }; diff --git a/src/gui/kernel/qsoftkeymanager_s60.cpp b/src/gui/kernel/qsoftkeymanager_s60.cpp index 7d643c2..2a9ac42 100644 --- a/src/gui/kernel/qsoftkeymanager_s60.cpp +++ b/src/gui/kernel/qsoftkeymanager_s60.cpp @@ -365,17 +365,30 @@ void QSoftKeyManagerPrivateS60::updateSoftKeys_sys() nativeContainer->DrawDeferred(); // 3.1 needs an extra invitation } +static void resetMenuBeingConstructed(TAny* /*aAny*/) +{ + S60->menuBeingConstructed = false; +} + +void QSoftKeyManagerPrivateS60::tryDisplayMenuBarL() +{ + CleanupStack::PushL(TCleanupItem(resetMenuBeingConstructed, NULL)); + S60->menuBeingConstructed = true; + S60->menuBar()->TryDisplayMenuBarL(); + CleanupStack::PopAndDestroy(); // Reset menuBeingConstructed to false in all cases +} + bool QSoftKeyManagerPrivateS60::handleCommand(int command) { QAction *action = realSoftKeyActions.value(command); if (action) { QVariant property = action->property(MENU_ACTION_PROPERTY); if (property.isValid() && property.toBool()) { - QT_TRAP_THROWING(S60->menuBar()->TryDisplayMenuBarL()); + QT_TRAP_THROWING(tryDisplayMenuBarL()); } else if (action->menu()) { // TODO: This is hack, in order to use exising QMenuBar implementation for Symbian // menubar needs to have widget to which it is associated. Since we want to associate - // menubar to action (which is inherited from QObejct), we create and associate QWidget + // menubar to action (which is inherited from QObject), we create and associate QWidget // to action and pass that for QMenuBar. This associates the menubar to action, and we // can have own menubar for each action. QWidget *actionContainer = action->property("_q_action_widget").value(); @@ -394,7 +407,7 @@ bool QSoftKeyManagerPrivateS60::handleCommand(int command) action->setProperty("_q_action_widget", v); } qt_symbian_next_menu_from_action(actionContainer); - QT_TRAP_THROWING(S60->menuBar()->TryDisplayMenuBarL()); + QT_TRAP_THROWING(tryDisplayMenuBarL()); } else { Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey); QWidget *actionParent = action->parentWidget(); diff --git a/src/gui/kernel/qsoftkeymanager_s60_p.h b/src/gui/kernel/qsoftkeymanager_s60_p.h index a5e5016..d14993c 100644 --- a/src/gui/kernel/qsoftkeymanager_s60_p.h +++ b/src/gui/kernel/qsoftkeymanager_s60_p.h @@ -78,6 +78,7 @@ public: bool handleCommand(int command); private: + void tryDisplayMenuBarL(); bool skipCbaUpdate(); void ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba); void clearSoftkeys(CEikButtonGroupContainer &cba); diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index 7c6b754..a714221 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -122,6 +122,7 @@ public: int qtOwnsS60Environment : 1; int supportsPremultipliedAlpha : 1; int avkonComponentsSupportTransparency : 1; + int menuBeingConstructed : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type static inline void updateScreenSize(); static inline RWsSession& wsSession(); -- cgit v0.12 From 383c336be79c9757a51427f06aa68df0b4849e31 Mon Sep 17 00:00:00 2001 From: Janne Koskinen Date: Thu, 8 Apr 2010 13:07:27 +0300 Subject: Clear QFontCache TLS content before nullifying TLS pointer. If not cleared server handles are left open causing Font Server to Panic with KErrInUse in Symbian. Task-number: QTBUG-9565 Reviewed-by: Simon Hausmann --- src/gui/text/qfont.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index dd9e69e..a41b000 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -2612,8 +2612,10 @@ void QFontCache::cleanup() } QT_CATCH (const std::bad_alloc &) { // no cache - just ignore } - if (cache && cache->hasLocalData()) + if (cache && cache->hasLocalData()) { + cache->localData()->clear(); cache->setLocalData(0); + } } #endif // QT_NO_THREAD -- cgit v0.12 From 0ef1365a07ca63b8243d8c051ebc7a8b7a7b5d5e Mon Sep 17 00:00:00 2001 From: Frans Englich Date: Thu, 8 Apr 2010 14:44:18 +0200 Subject: Symbian emulator: unload file server so apps can be recompiled. Mentioned in the Qt S60 team developer journal, 23.05.2008. Code & investigation by Janne Anttila. Reviewed-by: Janne Koskinen --- src/corelib/kernel/qcoreapplication.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 2da5a7d..102c41e 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -67,6 +67,7 @@ #ifdef Q_OS_SYMBIAN # include # include +# include # include "qeventdispatcher_symbian_p.h" # include "private/qcore_symbian_p.h" #elif defined(Q_OS_UNIX) @@ -579,6 +580,27 @@ void QCoreApplication::init() qt_core_eval_init(d->application_type); #endif +#if defined(Q_OS_SYMBIAN) \ + && defined(Q_CC_NOKIAX86) \ + && defined(QT_DEBUG) + /** + * Prevent the executable from being locked in the Symbian emulator. The + * code dramatically simplifies debugging on Symbian, but beyond that has + * no impact. + * + * Force the ZLazyUnloadTimer to fire and therefore unload code segments + * immediately. The code affects Symbian's file server and on the other + * hand needs only to be run once in each emulator run. + */ + { + RLoader loader; + CleanupClosePushL(loader); + User::LeaveIfError(loader.Connect()); + User::LeaveIfError(loader.CancelLazyDllUnload()); + CleanupStack::PopAndDestroy(&loader); + } +#endif + qt_startup_hook(); } -- cgit v0.12 From e86eabc78e222d9c46a38940085025dea518aefa Mon Sep 17 00:00:00 2001 From: Janne Anttila Date: Thu, 8 Apr 2010 13:55:21 +0300 Subject: Generate triggered signal even the action launches menu in Symbian. Triggered signal is useful for detecting native 'Options' menu launches in Symbian. QMenu::aboutToShow event is currently also not generated, but that is part of another bug report. And QMenu::aboutToShow would not even be generated for 'Options' menu itself but only for its sub/cascade menus. Task-number: QTBUG-9669 Reviewed-by: Sami Merila --- src/gui/kernel/qsoftkeymanager_s60.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gui/kernel/qsoftkeymanager_s60.cpp b/src/gui/kernel/qsoftkeymanager_s60.cpp index 2a9ac42..c0761f0 100644 --- a/src/gui/kernel/qsoftkeymanager_s60.cpp +++ b/src/gui/kernel/qsoftkeymanager_s60.cpp @@ -408,14 +408,14 @@ bool QSoftKeyManagerPrivateS60::handleCommand(int command) } qt_symbian_next_menu_from_action(actionContainer); QT_TRAP_THROWING(tryDisplayMenuBarL()); - } else { - Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey); - QWidget *actionParent = action->parentWidget(); - Q_ASSERT_X(actionParent, Q_FUNC_INFO, "No parent set for softkey action!"); - if (actionParent->isEnabled()) { - action->activate(QAction::Trigger); - return true; - } + } + + Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey); + QWidget *actionParent = action->parentWidget(); + Q_ASSERT_X(actionParent, Q_FUNC_INFO, "No parent set for softkey action!"); + if (actionParent->isEnabled()) { + action->activate(QAction::Trigger); + return true; } } return false; -- cgit v0.12 From 385f2c8a34a52443e78dfa272a1a483b9d6d29fc Mon Sep 17 00:00:00 2001 From: Jason Barron Date: Fri, 9 Apr 2010 13:21:32 +0200 Subject: Enable preserved swap behavior when surface is created due to resize. When the QVG_RECREATE_ON_SIZE_CHANGE macro is defined the EGL surface is recreated. However, after creating the surface, we were neglecting to set the surface attribute that enabled the preserved swapping behavior of EGL. This lead to flicker because every second frame was swapping with an empty buffer and only the dirty areas were being painted leaving the rest empty. Reviewed-by: Lars Knoll Reviewed-by: Aleksandar Sasha Babic Task-number: QT-3198 Task-number: QT-3184 Task-number: QT-3201 --- src/openvg/qwindowsurface_vgegl.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp index f46d6c2..46a905f 100644 --- a/src/openvg/qwindowsurface_vgegl.cpp +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -659,6 +659,7 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) #endif windowSurface = context->createSurface(widget, &surfaceProps); isPaintingActive = false; + needToSwap = true; } #else if (context && size != newSize) { @@ -710,20 +711,21 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) needToSwap = false; } #endif -#if !defined(QVG_NO_PRESERVED_SWAP) - // Try to force the surface back buffer to preserve its contents. - if (needToSwap) { - eglGetError(); // Clear error state first. - eglSurfaceAttrib(QEglContext::display(), surface, - EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); - if (eglGetError() != EGL_SUCCESS) { - qWarning("QVG: could not enable preserved swap"); - } - } -#endif windowSurface = surface; isPaintingActive = false; } + +#if !defined(QVG_NO_PRESERVED_SWAP) + // Try to force the surface back buffer to preserve its contents. + if (needToSwap) { + eglGetError(); // Clear error state first. + eglSurfaceAttrib(QEglContext::display(), windowSurface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + if (eglGetError() != EGL_SUCCESS) { + qWarning("QVG: could not enable preserved swap"); + } + } +#endif return context; } -- cgit v0.12