Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 9ab60b9 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 <[email protected]>
  • Loading branch information
Gabriel de Dietrich authored and Felix Bourbonnais committed Sep 4, 2018
1 parent cf41d17 commit 4a5a993
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#include "qcocoacolordialoghelper.h"
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"

#import <AppKit/AppKit.h>

Expand Down Expand Up @@ -318,6 +319,10 @@ - (BOOL)runApplicationModalPanel
// 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);
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class QCocoaEventDispatcher : public QAbstractEventDispatcher
void interrupt();
void flush();

static void clearCurrentThreadCocoaEventDispatcherInterruptFlag();

friend void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher);
};

Expand Down
13 changes: 13 additions & 0 deletions src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,19 @@ void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *even
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);
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "qt_mac_p.h"
#include "qcocoahelpers.h"
#include "qcocoamenubar.h"
#include "qcocoaeventdispatcher.h"
#include <qregexp.h>
#include <qbuffer.h>
#include <qdebug.h>
Expand Down Expand Up @@ -237,6 +238,10 @@ - (BOOL)runApplicationModalPanel
// 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();
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

#include "qcocoafontdialoghelper.h"
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"

#import <AppKit/AppKit.h>

Expand Down Expand Up @@ -313,6 +314,10 @@ - (BOOL)runApplicationModalPanel
// 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);
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/platforms/cocoa/qcocoanativeinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ public Q_SLOTS:
*/
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.
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/platforms/cocoa/qcocoanativeinterface.mm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "qcocoahelpers.h"
#include "qcocoaapplication.h"
#include "qcocoaintegration.h"
#include "qcocoaeventdispatcher.h"

#include <qbytearray.h>
#include <qwindow.h>
Expand Down Expand Up @@ -193,6 +194,11 @@
return QPixmap();
}

void QCocoaNativeInterface::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
{
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
}

void QCocoaNativeInterface::onAppFocusWindowChanged(QWindow *window)
{
Q_UNUSED(window);
Expand Down
5 changes: 5 additions & 0 deletions src/printsupport/dialogs/qpagesetupdialog_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ - (void)pageLayoutDidEnd:(NSPageLayout *)pageLayout
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 {
Expand Down
5 changes: 5 additions & 0 deletions src/printsupport/dialogs/qprintdialog_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ - (void)printPanelDidEnd:(NSPrintPanel *)printPanel
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 {
Expand Down

0 comments on commit 4a5a993

Please sign in to comment.