diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index ccbd3b8ce88..30eef9ff35f 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -15,6 +15,8 @@ #include "qcoreapplication_p.h" #include +#include "q26numeric.h" + QT_BEGIN_NAMESPACE #ifndef TIME_KILL_SYNCHRONOUS @@ -298,7 +300,7 @@ static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatch static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime) { - uint interval = t->interval; + qint64 interval = t->interval; ULONG tolerance = TIMERV_DEFAULT_COALESCING; switch (t->timerType) { case Qt::PreciseTimer: @@ -347,7 +349,13 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) bool ok = false; ULONG tolerance = calculateNextTimeout(t, qt_msectime()); - uint interval = t->interval; + uint interval = q26::saturate_cast(t->interval); + if (interval != t->interval) { + // Now we'll need to post the timer event at each second timeout. + interval = q26::saturate_cast(t->interval / 2 + 1); + t->usesExtendedInterval = true; + t->isSecondTimeout = false; + } if (interval == 0u) { // optimization for single-shot-zero-timer QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId)); @@ -390,6 +398,12 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId) { WinTimerInfo *t = timerDict.value(timerId); if (t && !t->inTimerEvent) { + if (t->usesExtendedInterval && !t->isSecondTimeout) { + // That's the case when the interval is larger than uint + t->isSecondTimeout = true; + return; + } + // send event, but don't allow it to recurse t->inTimerEvent = true; @@ -403,6 +417,7 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId) if (t->timerId == -1) { delete t; } else { + t->isSecondTimeout = false; t->inTimerEvent = false; } } @@ -702,6 +717,8 @@ void QEventDispatcherWin32::registerTimer(Qt::TimerId timerId, Duration interval t->obj = object; t->inTimerEvent = false; t->fastTimerId = 0; + t->usesExtendedInterval = false; + t->isSecondTimeout = false; d->registerTimer(t); diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index 3b3dd84ee9f..0efd4a83796 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -93,6 +93,8 @@ struct WinTimerInfo { // internal timer info Qt::TimerType timerType; UINT fastTimerId; bool inTimerEvent; + bool usesExtendedInterval; + bool isSecondTimeout; }; class QZeroTimerEvent : public QTimerEvent diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp index 370d5da91a6..22ad3d49286 100644 --- a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp +++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp @@ -284,6 +284,14 @@ void tst_QChronoTimer::remainingTimeInitial() } else { QCOMPARE_LE(rt, startTimeNs); } + + if (startTimeNs > 1min) { + // The test will surely take less than 1 minute + auto startMinusOneMinute = std::chrono::nanoseconds{startTimeNs - 1min}; + // If the remaining time is less than startMinusOneMinute, then + // something is terribly wrong with the internal interval calculations + QCOMPARE_GE(rt, startMinusOneMinute); + } } void tst_QChronoTimer::remainingTimeDuringActivation_data()