diff options
Diffstat (limited to 'src/corelib')
| -rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/corelib/animation/qabstractanimation.cpp | 4 | ||||
| -rw-r--r-- | src/corelib/animation/qabstractanimation_p.h | 8 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qwasmanimationdriver.cpp | 129 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qwasmanimationdriver_p.h | 54 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp | 132 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h | 33 | ||||
| -rw-r--r-- | src/corelib/tools/qcryptographichash.cpp | 2 |
8 files changed, 340 insertions, 23 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 55d375f0350..f31968f8199 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -1510,6 +1510,7 @@ endif() qt_internal_extend_target(Core CONDITION WASM SOURCES + platform/wasm/qwasmanimationdriver.cpp platform/wasm/qwasmanimationdriver_p.h platform/wasm/qwasmglobal.cpp platform/wasm/qwasmglobal_p.h platform/wasm/qstdweb.cpp platform/wasm/qstdweb_p.h platform/wasm/qwasmsocket.cpp platform/wasm/qwasmsocket_p.h diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 17814c6756a..c3e1ba4010f 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -113,6 +113,10 @@ #include "qabstractanimation_p.h" +#if defined(Q_OS_WASM) +#include <QtCore/private/qwasmanimationdriver_p.h> +#endif + #include <QtCore/qmath.h> #include <QtCore/qcoreevent.h> #include <QtCore/qpointer.h> diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index b4f462071a7..1eaa475f613 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -23,6 +23,10 @@ #include <private/qproperty_p.h> #include <qabstractanimation.h> +#if defined(Q_OS_WASM) +#include <QtCore/private/qwasmanimationdriver_p.h> +#endif + QT_REQUIRE_CONFIG(animation); QT_BEGIN_NAMESPACE @@ -184,7 +188,11 @@ private: friend class QAnimationDriver; QAnimationDriver *driver; +#if defined(Q_OS_WASM) + QWasmAnimationDriver defaultDriver; +#else QDefaultAnimationDriver defaultDriver; +#endif QBasicTimer pauseTimer; diff --git a/src/corelib/platform/wasm/qwasmanimationdriver.cpp b/src/corelib/platform/wasm/qwasmanimationdriver.cpp new file mode 100644 index 00000000000..ab0c8240dd1 --- /dev/null +++ b/src/corelib/platform/wasm/qwasmanimationdriver.cpp @@ -0,0 +1,129 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwasmanimationdriver_p.h" +#include "qwasmsuspendresumecontrol_p.h" + +#include <emscripten/val.h> + +QT_BEGIN_NAMESPACE + + +// QWasmAnimationDriver drives animations using requestAnimationFrame(). This +// ensures that animations are advanced in sync with frame update calls, which +// again are synced to the screen refresh rate. + +namespace { + constexpr int FallbackTimerInterval = 500; +} + +QWasmAnimationDriver::QWasmAnimationDriver(QUnifiedTimer *) + : QAnimationDriver(nullptr) +{ + connect(this, &QAnimationDriver::started, this, &QWasmAnimationDriver::start); + connect(this, &QAnimationDriver::stopped, this, &QWasmAnimationDriver::stop); +} + +QWasmAnimationDriver::~QWasmAnimationDriver() +{ + disconnect(this, &QAnimationDriver::started, this, &QWasmAnimationDriver::start); + disconnect(this, &QAnimationDriver::stopped, this, &QWasmAnimationDriver::stop); + + if (m_animateCallbackHandle != 0) + QWasmAnimationFrameMultiHandler::instance()->unregisterAnimateCallback(m_animateCallbackHandle); +} + +qint64 QWasmAnimationDriver::elapsed() const +{ + return isRunning() ? qint64(m_currentTimestamp - m_startTimestamp) : 0; +} + +double QWasmAnimationDriver::getCurrentTimeFromTimeline() const +{ + // Get the current timeline time, which is an equivalent time source to the + // animation frame time source. According to the documentation this API + // may be unavailable in various cases; check for null before accessing. + emscripten::val document = emscripten::val::global("document"); + emscripten::val timeline = document["timeline"]; + if (!timeline.isNull() && !timeline.isUndefined()) { + emscripten::val currentTime = timeline["currentTime"]; + if (!currentTime.isNull() && !currentTime.isUndefined()) + return currentTime.as<double>(); + } + return 0; +} + +void QWasmAnimationDriver::handleFallbackTimeout() +{ + if (!isRunning()) + return; + + // Get the current time from a timing source equivalent to the animation frame time + double currentTime = getCurrentTimeFromTimeline(); + if (currentTime == 0) + currentTime = m_currentTimestamp + FallbackTimerInterval; + const double timeSinceLastFrame = currentTime - m_currentTimestamp; + + // Advance animations if animations are active but there has been no rcent animation + // frame callback. + if (timeSinceLastFrame > FallbackTimerInterval * 0.8) { + m_currentTimestamp = currentTime; + advance(); + } +} + +void QWasmAnimationDriver::start() +{ + if (isRunning()) + return; + + // Set start timestamp to document.timeline.currentTime() + m_startTimestamp = getCurrentTimeFromTimeline(); + m_currentTimestamp = m_startTimestamp; + + // Register animate callback + m_animateCallbackHandle = QWasmAnimationFrameMultiHandler::instance()->registerAnimateCallback( + [this](double timestamp) { handleAnimationFrame(timestamp); }); + + // Start fallback timer to ensure animations advance even if animaton frame callbacks stop coming + fallbackTimer.setInterval(FallbackTimerInterval); + connect(&fallbackTimer, &QTimer::timeout, this, &QWasmAnimationDriver::handleFallbackTimeout); + fallbackTimer.start(); + + QAnimationDriver::start(); +} + +void QWasmAnimationDriver::stop() +{ + m_startTimestamp = 0; + m_currentTimestamp = 0; + + // Stop and disconnect the fallback timer + fallbackTimer.stop(); + disconnect(&fallbackTimer, &QTimer::timeout, this, &QWasmAnimationDriver::handleFallbackTimeout); + + // Deregister the animation frame callback + if (m_animateCallbackHandle != 0) { + QWasmAnimationFrameMultiHandler::instance()->unregisterAnimateCallback(m_animateCallbackHandle); + m_animateCallbackHandle = 0; + } + + QAnimationDriver::stop(); +} + +void QWasmAnimationDriver::handleAnimationFrame(double timestamp) +{ + if (!isRunning()) + return; + + m_currentTimestamp = timestamp; + + // Fall back to setting m_startTimestamp here in cases where currentTime + // was not available in start() (gives 0 elapsed time for the first frame) + if (m_startTimestamp == 0) + m_startTimestamp = timestamp; + + advance(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/platform/wasm/qwasmanimationdriver_p.h b/src/corelib/platform/wasm/qwasmanimationdriver_p.h new file mode 100644 index 00000000000..f8435c17a9a --- /dev/null +++ b/src/corelib/platform/wasm/qwasmanimationdriver_p.h @@ -0,0 +1,54 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWASMANIMATIONDRIVER_P_H +#define QWASMANIMATIONDRIVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <QtCore/qabstractanimation.h> +#include <QtCore/qtimer.h> + +#include <memory> + +QT_BEGIN_NAMESPACE + +class QUnifiedTimer; + +class Q_CORE_EXPORT QWasmAnimationDriver : public QAnimationDriver +{ + Q_OBJECT +public: + QWasmAnimationDriver(QUnifiedTimer *unifiedTimer); + ~QWasmAnimationDriver() override; + + qint64 elapsed() const override; + +protected: + void start() override; + void stop() override; + +private: + void handleAnimationFrame(double timestamp); + void handleFallbackTimeout(); + double getCurrentTimeFromTimeline() const; + + QTimer fallbackTimer; + uint32_t m_animateCallbackHandle = 0; + double m_startTimestamp = 0; + double m_currentTimestamp = 0; +}; + +QT_END_NAMESPACE + +#endif // QWASMANIMATIONDRIVER_P_H diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp index 093898c520a..5fe92926240 100644 --- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp +++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp @@ -4,6 +4,8 @@ #include "qwasmsuspendresumecontrol_p.h" #include "qstdweb_p.h" +#include <QtCore/qapplicationstatic.h> + #include <emscripten.h> #include <emscripten/val.h> #include <emscripten/bind.h> @@ -75,32 +77,20 @@ void qtRegisterEventHandlerJs(int index) { }[name]; } - function deepShallowClone(parent, obj, depth) { + function deepShallowClone(obj) { if (obj === null) return obj; - if (typeof obj === 'function') { - if (obj.name !== "") - return createNamedFunction(obj.name, parent, obj); - } - - if (depth >= 1) - return obj; - - if (typeof obj !== 'object') + if (!(obj instanceof Event)) return obj; - if (Array.isArray(obj)) { - const arrCopy = []; - for (let i = 0; i < obj.length; i++) - arrCopy[i] = deepShallowClone(obj, obj[i], depth + 1); - - return arrCopy; - } - const objCopy = {}; - for (const key in obj) - objCopy[key] = deepShallowClone(obj, obj[key], depth + 1); + for (const key in obj) { + if (typeof obj[key] === 'function') + objCopy[key] = createNamedFunction(obj[key].name, obj, obj[key]); + else + objCopy[key] = obj[key]; + } return objCopy; } @@ -110,7 +100,7 @@ void qtRegisterEventHandlerJs(int index) { let handler = (arg) => { // Copy the top level object, alias the rest. // functions are copied by creating new forwarding functions. - arg = deepShallowClone(arg, arg, 0); + arg = deepShallowClone(arg); // Add event to event queue control.pendingEvents.push({ @@ -357,3 +347,103 @@ void QWasmTimer::clearTimeout() val::global("window").call<void>("clearTimeout", double(m_timerId)); m_timerId = 0; } + +// +// QWasmAnimationFrameMultiHandler +// +// Multiplexes multiple animate and draw callbacks to a single native requestAnimationFrame call. +// Animate callbacks are called before draw callbacks to ensure animations are advanced before drawing. +// +QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler() +{ + auto wrapper = [this](val arg) { + handleAnimationFrame(arg.as<double>()); + }; + m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper); +} + +QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler() +{ + cancelAnimationFrameRequest(); + QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex); +} + +Q_APPLICATION_STATIC(QWasmAnimationFrameMultiHandler, s_animationFrameHandler); +QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance() +{ + return s_animationFrameHandler(); +} + +// Registers a permanent animation callback. Call unregisterAnimateCallback() to unregister +uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback) +{ + uint32_t handle = ++m_nextAnimateHandle; + m_animateCallbacks[handle] = std::move(callback); + ensureAnimationFrameRequested(); + return handle; +} + +// Registers a single-shot draw callback. +uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback) +{ + uint32_t handle = ++m_nextDrawHandle; + m_drawCallbacks[handle] = std::move(callback); + ensureAnimationFrameRequested(); + return handle; +} + +void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle) +{ + m_animateCallbacks.erase(handle); + if (m_animateCallbacks.empty() && m_drawCallbacks.empty()) + cancelAnimationFrameRequest(); +} + +void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle) +{ + m_drawCallbacks.erase(handle); + if (m_animateCallbacks.empty() && m_drawCallbacks.empty()) + cancelAnimationFrameRequest(); +} + +void QWasmAnimationFrameMultiHandler::handleAnimationFrame(double timestamp) +{ + m_requestId = -1; + + // Advance animations. Copy the callbacks list in case callbacks are + // unregistered during iteration + auto animateCallbacksCopy = m_animateCallbacks; + for (const auto &pair : animateCallbacksCopy) + pair.second(timestamp); + + // Draw the frame. Note that draw callbacks are cleared after each + // frame, matching QWindow::requestUpdate() behavior. Copy the callbacks + // list in case new callbacks are registered while drawing the frame + auto drawCallbacksCopy = m_drawCallbacks; + m_drawCallbacks.clear(); + for (const auto &pair : drawCallbacksCopy) + pair.second(timestamp); + + // Request next frame if there are still callbacks registered + if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty()) + ensureAnimationFrameRequested(); +} + +void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested() +{ + if (m_requestId != -1) + return; + + using ReturnType = double; + val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex); + m_requestId = int64_t(val::global("window").call<ReturnType>("requestAnimationFrame", handler)); +} + +void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest() +{ + if (m_requestId == -1) + return; + + val::global("window").call<void>("cancelAnimationFrame", double(m_requestId)); + m_requestId = -1; +} diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h index 37c71ed8123..b750d80314c 100644 --- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h +++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h @@ -83,4 +83,37 @@ private: uint64_t m_timerId = 0; }; +class Q_CORE_EXPORT QWasmAnimationFrameMultiHandler +{ +public: + using Callback = std::function<void(double)>; + + static QWasmAnimationFrameMultiHandler *instance(); + + uint32_t registerAnimateCallback(Callback callback); + uint32_t registerDrawCallback(Callback callback); + + void unregisterAnimateCallback(uint32_t handle); + void unregisterDrawCallback(uint32_t handle); + + QWasmAnimationFrameMultiHandler(); + ~QWasmAnimationFrameMultiHandler(); + QWasmAnimationFrameMultiHandler(const QWasmAnimationFrameMultiHandler&) = delete; + QWasmAnimationFrameMultiHandler& operator=(const QWasmAnimationFrameMultiHandler&) = delete; + +private: + void handleAnimationFrame(double timestamp); + void ensureAnimationFrameRequested(); + void cancelAnimationFrameRequest(); + + static QWasmAnimationFrameMultiHandler *s_instance; + + std::map<uint32_t, Callback> m_animateCallbacks; + std::map<uint32_t, Callback> m_drawCallbacks; + uint32_t m_nextAnimateHandle = 0; + uint32_t m_nextDrawHandle = 0; + uint32_t m_handlerIndex = 0; + int64_t m_requestId = -1; +}; + #endif diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 092ff46b084..53cca38ba7b 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -101,8 +101,6 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt } #endif // !QT_CONFIG(opensslv30) -#include "qtcore-config_p.h" - #if QT_CONFIG(system_libb2) #include <blake2.h> #else |
