diff options
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_win.cpp | 67 | ||||
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_win_p.h | 2 | ||||
-rw-r--r-- | tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp | 31 |
3 files changed, 67 insertions, 33 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index c15d740f9e..87623f304a 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -100,7 +100,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QEventDispatcherWin32Private::QEventDispatcherWin32Private() : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), - wakeUps(0), activateNotifiersPosted(false), + getMessageHook(0), wakeUps(0), activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL) { } @@ -245,9 +245,6 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA case WM_QT_SENDPOSTEDEVENTS: Q_ASSERT(d != 0); - // Allow posting WM_QT_SENDPOSTEDEVENTS message. - d->wakeUps.storeRelaxed(0); - // We send posted events manually, if the window procedure was invoked // by the foreign event loop (e.g. from the native modal dialog). q->sendPostedEvents(); @@ -257,9 +254,9 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA return DefWindowProc(hwnd, message, wp, lp); } -static inline UINT inputTimerMask() +static inline UINT inputQueueMask() { - UINT result = QS_TIMER | QS_INPUT | QS_RAWINPUT; + UINT result = QS_ALLEVENTS; // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8. #if WINVER > 0x0601 @@ -269,6 +266,25 @@ static inline UINT inputTimerMask() return result; } +LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) +{ + QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance()); + Q_ASSERT(q != 0); + QEventDispatcherWin32Private *d = q->d_func(); + MSG *msg = reinterpret_cast<MSG *>(lp); + static const UINT mask = inputQueueMask(); + + if (HIWORD(GetQueueStatus(mask)) == 0 && wp == PM_REMOVE) { + // Allow posting WM_QT_SENDPOSTEDEVENTS message. + d->wakeUps.storeRelaxed(0); + if (!(msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS)) { + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, + WMWP_QT_TOFOREIGNLOOP, 0); + } + } + return d->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0; +} + // Provide class name and atom for the message window used by // QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads. struct QWindowsMessageWindowClassContext @@ -447,6 +463,14 @@ void QEventDispatcherWin32::createInternalHwnd() return; d->internalHwnd = qt_create_internal_window(this); + // setup GetMessage hook needed to drive our posted events + d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); + if (Q_UNLIKELY(!d->getMessageHook)) { + int errorCode = GetLastError(); + qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls", + errorCode, qUtf16Printable(qt_error_string(errorCode))); + } + // start all normal timers for (int i = 0; i < d->timerVec.count(); ++i) d->registerTimer(d->timerVec.at(i)); @@ -499,7 +523,6 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) bool canWait; bool retVal = false; - bool needWM_QT_SENDPOSTEDEVENTS = false; do { DWORD waitRet = 0; DWORD nCount = 0; @@ -549,11 +572,8 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) if (haveMessage) { if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { // Set result to 'true', if the message was sent by wakeUp(). - if (msg.wParam == WMWP_QT_FROMWAKEUP) { - d->wakeUps.storeRelaxed(0); + if (msg.wParam == WMWP_QT_FROMWAKEUP) retVal = true; - } - needWM_QT_SENDPOSTEDEVENTS = true; continue; } if (msg.message == WM_TIMER) { @@ -573,22 +593,10 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) } if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { - // Post WM_QT_SENDPOSTEDEVENTS before calling external code, - // as it can start a foreign event loop. - if (needWM_QT_SENDPOSTEDEVENTS) { - needWM_QT_SENDPOSTEDEVENTS = false; - PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, - WMWP_QT_TOFOREIGNLOOP, 0); - } TranslateMessage(&msg); DispatchMessage(&msg); } } else if (waitRet - WAIT_OBJECT_0 < nCount) { - if (needWM_QT_SENDPOSTEDEVENTS) { - needWM_QT_SENDPOSTEDEVENTS = false; - PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, - WMWP_QT_TOFOREIGNLOOP, 0); - } activateEventNotifiers(); } else { // nothing todo so break @@ -606,21 +614,12 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); if (waitRet - WAIT_OBJECT_0 < nCount) { - if (needWM_QT_SENDPOSTEDEVENTS) { - needWM_QT_SENDPOSTEDEVENTS = false; - PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, - WMWP_QT_TOFOREIGNLOOP, 0); - } activateEventNotifiers(); retVal = true; } } } while (canWait); - if (needWM_QT_SENDPOSTEDEVENTS) - PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, - WMWP_QT_TOFOREIGNLOOP, 0); - return retVal; } @@ -1004,6 +1003,10 @@ void QEventDispatcherWin32::closingDown() d->timerDict.clear(); d->closingDown = true; + + if (d->getMessageHook) + UnhookWindowsHookEx(d->getMessageHook); + d->getMessageHook = 0; } bool QEventDispatcherWin32::event(QEvent *e) diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index 697c07f912..e6620178d8 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -113,6 +113,7 @@ protected: private: friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); + friend LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int, WPARAM, LPARAM); }; struct QSockNot { @@ -166,6 +167,7 @@ public: // internal window handle used for socketnotifiers/timers/etc HWND internalHwnd; + HHOOK getMessageHook; // for controlling when to send posted events QAtomicInt wakeUps; diff --git a/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp b/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp index 19c5c8a4a0..3d1876f00f 100644 --- a/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp +++ b/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp @@ -36,6 +36,7 @@ #include <QtNetwork/qtcpserver.h> #include <QtNetwork/qtcpsocket.h> #include <QtCore/qelapsedtimer.h> +#include <QtCore/qtimer.h> #include <QtCore/qt_windows.h> @@ -49,7 +50,7 @@ class tst_NoQtEventLoop : public QObject private slots: void consumeMouseEvents(); void consumeSocketEvents(); - + void deliverEventsInLivelock(); }; class Window : public QRasterWindow @@ -312,6 +313,34 @@ void tst_NoQtEventLoop::consumeSocketEvents() QVERIFY(server.hasPendingConnections()); } +void tst_NoQtEventLoop::deliverEventsInLivelock() +{ + int argc = 1; + char *argv[] = { const_cast<char *>("test"), 0 }; + QGuiApplication app(argc, argv); + + QTimer livelockTimer; + livelockTimer.start(0); + QTimer::singleShot(100, Qt::CoarseTimer, &livelockTimer, &QTimer::stop); + + QElapsedTimer elapsedTimer; + elapsedTimer.start(); + + // Exec own message loop + MSG msg; + forever { + if (elapsedTimer.hasExpired(3000) || !livelockTimer.isActive()) + break; + + if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } + + QVERIFY(!livelockTimer.isActive()); +} + #include <tst_noqteventloop.moc> QTEST_APPLESS_MAIN(tst_NoQtEventLoop) |