summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/animation/qabstractanimation.cpp4
-rw-r--r--src/corelib/animation/qabstractanimation_p.h8
-rw-r--r--src/corelib/platform/wasm/qwasmanimationdriver.cpp129
-rw-r--r--src/corelib/platform/wasm/qwasmanimationdriver_p.h54
-rw-r--r--src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp132
-rw-r--r--src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h33
-rw-r--r--src/corelib/tools/qcryptographichash.cpp2
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