processEvents(WaitForMoreEvents) should return after delivering events

The Cocoa event dispatcher sends timer, socket, and posted events
correctly, but they are not NSEvents, and as such, they do not cause
[NSApp nextEventMatchingMask] to return. When calling processEvents()
with WaitForMoreEvents, but the EventLoopExec flag isn't set, we want to
interrupt the WaitForMoreEvents.

As a result, We should not call wakeUp() at the top of processEvents(),
otherwise we end up shortcutting other event sources. We also do not
need to call QCoreApplication::sendPostedEvents() directly either in
processEvents(), it's the postedEventsSource job to do that.

The interruptLater mechanism is always run when calling processEvents()
directly (not via exec()), which causes problems when testing
processEvents(). Don't use interruptLater unless the modal sessions
change (which is indicated by the cached session pointer being reset
to zero).

Change-Id: Iec2b49a4f306b2702c979522f12a28d0b5fbd0b4
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@nokia.com>
This commit is contained in:
Bradley T. Hughes 2012-01-23 08:59:27 +01:00 committed by Qt by Nokia
parent da5d9e664f
commit b7ca6a81db
2 changed files with 28 additions and 11 deletions

View File

@ -149,6 +149,8 @@ class QCocoaEventDispatcherPrivate : public QAbstractEventDispatcherPrivate
public:
QCocoaEventDispatcherPrivate();
uint processEventsFlags;
// timer handling
QTimerInfoList timerInfoList;
CFRunLoopTimerRef runLoopTimerRef;
@ -173,6 +175,7 @@ public:
void beginModalSession(QWindow *widget);
void endModalSession(QWindow *widget);
void cancelWaitForMoreEvents();
void maybeCancelWaitForMoreEvents();
void cleanupModalSessions();
void ensureNSAppInitialized();

View File

@ -114,6 +114,7 @@ void QCocoaEventDispatcherPrivate::activateTimer(CFRunLoopTimerRef, void *info)
QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info);
(void) d->timerInfoList.activateTimers();
d->maybeStartCFRunLoopTimer();
d->maybeCancelWaitForMoreEvents();
}
void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer()
@ -275,6 +276,8 @@ void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CF
if (socketInfo->writeNotifier)
QGuiApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
}
eventDispatcher->maybeCancelWaitForMoreEvents();
}
/*
@ -508,13 +511,12 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
bool interruptLater = false;
QtCocoaInterruptDispatcher::cancelInterruptLater();
// In case we end up recursing while we now process events, make sure
// that we send remaining posted Qt events before this call returns:
wakeUp();
emit awake();
bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents;
bool retVal = false;
uint oldflags = d->processEventsFlags;
d->processEventsFlags = flags;
forever {
if (d->interrupt)
break;
@ -567,6 +569,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
}
retVal = true;
} else {
bool hadModalSession = d->currentModalSessionCached != 0;
// We cannot block the thread (and run in a tight loop).
// Instead we will process all current pending events and return.
d->ensureNSAppInitialized();
@ -628,17 +631,13 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
}
} while (!d->interrupt && event != nil);
// Be sure to flush the Qt posted events when not using exec mode
// (exec mode will always do this call from the event loop source):
if (!d->interrupt)
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
// Since the window that holds modality might have changed while processing
// events, we we need to interrupt when we return back the previous process
// event recursion to ensure that we spin the correct modal session.
// We do the interruptLater at the end of the function to ensure that we don't
// disturb the 'wait for more events' below (as deleteLater will post an event):
interruptLater = true;
if (hadModalSession && d->currentModalSessionCached == 0)
interruptLater = true;
}
bool canWait = (d->threadData->canWait
&& !retVal
@ -656,6 +655,8 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
}
}
d->processEventsFlags = oldflags;
// If we're interrupted, we need to interrupt the _current_
// recursion as well to check if it is still supposed to be
// executing. This way we wind down the stack until we land
@ -878,7 +879,8 @@ void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window)
}
QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate()
: runLoopTimerRef(0),
: processEventsFlags(0),
runLoopTimerRef(0),
blockSendPostedEvents(false),
currentExecIsNSAppRun(false),
nsAppRunCalledByQt(false),
@ -1007,7 +1009,9 @@ void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
void QCocoaEventDispatcherPrivate::postedEventsSourcePerformCallback(void *info)
{
static_cast<QCocoaEventDispatcherPrivate *>(info)->processPostedEvents();
QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info);
d->processPostedEvents();
d->maybeCancelWaitForMoreEvents();
}
void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents()
@ -1020,6 +1024,16 @@ void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents()
subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO];
}
void QCocoaEventDispatcherPrivate::maybeCancelWaitForMoreEvents()
{
if ((processEventsFlags & (QEventLoop::EventLoopExec | QEventLoop::WaitForMoreEvents)) == QEventLoop::WaitForMoreEvents) {
// RunLoop sources are not NSEvents, but they do generate Qt events. If
// WaitForMoreEvents was set, but EventLoopExec is not, processEvents()
// should return after a source has sent some Qt events.
cancelWaitForMoreEvents();
}
}
void QCocoaEventDispatcher::interrupt()
{
Q_D(QCocoaEventDispatcher);