macOS: Clear event dispatcher interrupt state

A pending interrupt of a QEventLoop may interfere with
native runModal calls, resulting in Cocoa's main event
loop to be stopped unexpectedly.

After commit 9ab60b9c processEvents() no longer resets
the event dispatcher interrupt flag.

Add QCocoaEventDispatcher::clearCurrentThreadCocoa
EventDispatcherInterruptFlag(). Use it to clear the
interrupt state before calling runModal and variants.

Work around the inability to use platform API in
the print support code.

Change-Id: I52f26f99a63cbb46969db42f65b09a3c3119ad15
Task-number: QTBUG-56746
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
This commit is contained in:
Gabriel de Dietrich 2016-11-01 15:28:17 -07:00 committed by Morten Johan Sørvig
parent ffe72840a3
commit ae8d3d69d6
9 changed files with 48 additions and 0 deletions

View File

@ -39,6 +39,7 @@
#include <QtCore/qtimer.h>
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"
#import <AppKit/AppKit.h>
@ -318,6 +319,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
// Make sure we don't interrupt the runModalForWindow call.
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
[NSApp runModalForWindow:mColorPanel];
mDialogIsExecuting = false;
return (mResultCode == NSOKButton);

View File

@ -125,6 +125,8 @@ public:
void interrupt();
void flush();
static void clearCurrentThreadCocoaEventDispatcherInterruptFlag();
friend void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher);
};

View File

@ -960,6 +960,19 @@ void QCocoaEventDispatcher::interrupt()
void QCocoaEventDispatcher::flush()
{ }
// QTBUG-56746: The behavior of processEvents() has been changed to not clear
// the interrupt flag. Use this function to clear it.
void QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
{
QCocoaEventDispatcher *cocoaEventDispatcher =
qobject_cast<QCocoaEventDispatcher *>(QThread::currentThread()->eventDispatcher());
if (!cocoaEventDispatcher)
return;
QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate =
static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
cocoaEventDispatcherPrivate->interrupt = false;
}
QCocoaEventDispatcher::~QCocoaEventDispatcher()
{
Q_D(QCocoaEventDispatcher);

View File

@ -45,6 +45,7 @@
#include "qt_mac_p.h"
#include "qcocoahelpers.h"
#include "qcocoamenubar.h"
#include "qcocoaeventdispatcher.h"
#include <qregexp.h>
#include <qbuffer.h>
#include <qdebug.h>
@ -235,6 +236,10 @@ static QString strippedText(QString s)
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
// Make sure we don't interrupt the runModal call below.
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder();
mReturnCode = [mSavePanel runModal];
QCocoaMenuBar::resetKnownMenuItemsToQt();

View File

@ -43,6 +43,7 @@
#include <private/qfontengine_coretext_p.h>
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"
#import <AppKit/AppKit.h>
@ -313,6 +314,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
// Make sure we don't interrupt the runModalForWindow call.
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
[NSApp runModalForWindow:mFontPanel];
mDialogIsExecuting = false;
return (mResultCode == NSOKButton);

View File

@ -96,6 +96,8 @@ private:
*/
Q_INVOKABLE QPixmap defaultBackgroundPixmapForQWizard();
Q_INVOKABLE void clearCurrentThreadCocoaEventDispatcherInterruptFlag();
// QMacPastebardMime support. The mac pasteboard void pointers are
// QMacPastebardMime instances from the cocoa plugin or qtmacextras
// These two classes are kept in sync and can be casted between.

View File

@ -38,6 +38,7 @@
#include "qcocoahelpers.h"
#include "qcocoaapplication.h"
#include "qcocoaintegration.h"
#include "qcocoaeventdispatcher.h"
#include <qbytearray.h>
#include <qwindow.h>
@ -193,6 +194,11 @@ QPixmap QCocoaNativeInterface::defaultBackgroundPixmapForQWizard()
return QPixmap();
}
void QCocoaNativeInterface::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
{
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
}
void QCocoaNativeInterface::onAppFocusWindowChanged(QWindow *window)
{
Q_UNUSED(window);

View File

@ -127,6 +127,11 @@ void QMacPageSetupDialogPrivate::openCocoaPageLayout(Qt::WindowModality modality
QT_MANGLE_NAMESPACE(QCocoaPageLayoutDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QCocoaPageLayoutDelegate) alloc] initWithNSPrintInfo:printInfo];
if (modality == Qt::ApplicationModal) {
// Make sure we don't interrupt the runModalWithPrintInfo call.
(void) QMetaObject::invokeMethod(qApp->platformNativeInterface(),
"clearCurrentThreadCocoaEventDispatcherInterruptFlag");
int rval = [pageLayout runModalWithPrintInfo:printInfo];
[delegate pageLayoutDidEnd:pageLayout returnCode:rval contextInfo:q];
} else {

View File

@ -239,6 +239,11 @@ void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality)
if (modality == Qt::ApplicationModal || !q->parentWidget()) {
if (modality == Qt::NonModal)
qWarning("QPrintDialog is required to be modal on OS X");
// Make sure we don't interrupt the runModalWithPrintInfo call.
(void) QMetaObject::invokeMethod(qApp->platformNativeInterface(),
"clearCurrentThreadCocoaEventDispatcherInterruptFlag");
int rval = [printPanel runModalWithPrintInfo:printInfo];
[delegate printPanelDidEnd:printPanel returnCode:rval contextInfo:q];
} else {