QEventDispatcherWin32: retrieve PM_REMOVE value as a bit flag

Windows unexpectedly passes PM_NOYIELD flag in wParam parameter to the
hook procedure, if ::PeekMessage(..., PM_REMOVE | PM_NOYIELD) is called
from the event loop. So, to ignore undocumented flag, we should
interpret wParam as a bit field.

Thanks to Robin Lobel for research.

Pick-to: 5.15
Fixes: QTBUG-84562
Change-Id: Ib16d7d747aebc9a3628e4ee67478c4d3edeb96f1
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Alex Trotsenko 2020-06-02 18:03:36 +03:00
parent b99ade85b0
commit 33e6e5fac3
2 changed files with 43 additions and 1 deletions

View File

@ -277,9 +277,13 @@ LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
Q_ASSERT(q != 0);
QEventDispatcherWin32Private *d = q->d_func();
MSG *msg = reinterpret_cast<MSG *>(lp);
// Windows unexpectedly passes PM_NOYIELD flag to the hook procedure,
// if ::PeekMessage(..., PM_REMOVE | PM_NOYIELD) is called from the event loop.
// So, retrieve 'removed' tag as a bit field.
const bool messageRemoved = (wp & PM_REMOVE) != 0;
if (msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS
&& wp == PM_REMOVE && d->sendPostedEventsTimerId == 0) {
&& messageRemoved && d->sendPostedEventsTimerId == 0) {
// Start a timer to deliver posted events when the message queue is emptied.
d->sendPostedEventsTimerId = SetTimer(d->internalHwnd, SendPostedEventsTimerId,
USER_TIMER_MINIMUM, NULL);

View File

@ -51,6 +51,7 @@ private slots:
void consumeMouseEvents();
void consumeSocketEvents();
void deliverEventsInLivelock();
void postingWithNoYieldFlag();
};
class Window : public QRasterWindow
@ -341,6 +342,43 @@ void tst_NoQtEventLoop::deliverEventsInLivelock()
QVERIFY(!livelockTimer.isActive());
}
void tst_NoQtEventLoop::postingWithNoYieldFlag()
{
int argc = 1;
char *argv[] = { const_cast<char *>("test"), 0 };
QGuiApplication app(argc, argv);
bool signalReceived = false;
// Post a message to the queue
QMetaObject::invokeMethod(this, [&signalReceived]() {
signalReceived = true;
}, Qt::QueuedConnection);
// Post some system messages
QWindow mainWindow;
mainWindow.show();
QElapsedTimer elapsedTimer;
elapsedTimer.start();
// Exec own message loop
MSG msg;
forever {
if (elapsedTimer.hasExpired(3000) || signalReceived)
break;
if (!::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
QThread::msleep(100);
continue;
}
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
QVERIFY(signalReceived);
}
#include <tst_noqteventloop.moc>
QTEST_APPLESS_MAIN(tst_NoQtEventLoop)