summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2021-09-12 20:37:42 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2021-12-23 11:21:57 +0100
commit23964ce05f37fda4594c7eb33162e9bc1be962fc (patch)
tree987dc297304b3f2788219ad9124c8ff88783d04c /src
parente98f5de6e1a19e06cc9e94ea01ca27d9a65f8d70 (diff)
wasm: Use new event dispatcher for QtGui
The event dispatcher implementation is now in QtCore, except for the call to QWindowSystemInterface::sendWindowSystemEvents(). Implement QWasmWindow::requestUpdate() using emscripten_request_animation_frame(), instead of the previous registerRequestUpdateCallback() function which now is removed. Pick-to: 6.3 Change-Id: I7a13eb5391d48dba0f2afe4704ef3188b8daa74b Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp194
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.h27
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp20
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h1
5 files changed, 18 insertions, 230 deletions
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
index 8f9eeecbb0..d4b4bce462 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -29,197 +29,11 @@
#include "qwasmeventdispatcher.h"
-#include <QtCore/qcoreapplication.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
-#include <emscripten.h>
-
-#if QT_CONFIG(thread)
-#if (__EMSCRIPTEN_major__ > 1 || __EMSCRIPTEN_minor__ > 38 || __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ >= 22)
-# define EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
-#endif
-#endif
-
-#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
-#include <emscripten/threading.h>
-#endif
-
-class QWasmEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
-{
-
-};
-
-QWasmEventDispatcher *g_htmlEventDispatcher;
-
-QWasmEventDispatcher::QWasmEventDispatcher(QObject *parent)
- : QUnixEventDispatcherQPA(parent)
-{
-
- g_htmlEventDispatcher = this;
-}
-
-QWasmEventDispatcher::~QWasmEventDispatcher()
-{
- g_htmlEventDispatcher = nullptr;
-}
-
-bool QWasmEventDispatcher::registerRequestUpdateCallback(std::function<void(void)> callback)
-{
- if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
- return false;
-
- g_htmlEventDispatcher->m_requestUpdateCallbacks.append(callback);
- emscripten_resume_main_loop();
- return true;
-}
-
-void QWasmEventDispatcher::maintainTimers()
-{
- if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
- return;
-
- g_htmlEventDispatcher->doMaintainTimers();
-}
-
-bool QWasmEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
-{
- // WaitForMoreEvents is not supported (except for in combination with EventLoopExec below),
- // and we don't want the unix event dispatcher base class to attempt to wait either.
- flags &= ~QEventLoop::WaitForMoreEvents;
-
- // Handle normal processEvents.
- if (!(flags & QEventLoop::EventLoopExec))
- return QUnixEventDispatcherQPA::processEvents(flags);
-
- if (flags & QEventLoop::DialogExec) {
- qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly, please use"
- << "show() instead. When using exec() the dialog will show, the user can interact"
- << "with it and the appropriate signals will be emitted on close. However, the"
- << "exec() call never returns, stack content at the time of the exec() call"
- << "is leaked, and the exec() call may interfere with input event processing";
-
- emscripten_sleep(1); // This call never returns
- }
-
- // Handle processEvents from QEventLoop::exec():
- //
- // At this point the application has created its root objects on
- // the stack and has called app.exec() which has called into this
- // function via QEventLoop.
- //
- // The application now expects that exec() will not return until
- // app exit time. However, the browser expects that we return
- // control to it periodically, also after initial setup in main().
-
- // EventLoopExec for nested event loops is not supported.
- Q_ASSERT(!m_hasMainLoop);
- m_hasMainLoop = true;
-
- // Call emscripten_set_main_loop_arg() with a callback which processes
- // events. Also set simulateInfiniteLoop to true which makes emscripten
- // return control to the browser without unwinding the C++ stack.
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
-
- // Save and clear updateRequest callbacks so we can register new ones
- auto requestUpdateCallbacksCopy = that->m_requestUpdateCallbacks;
- that->m_requestUpdateCallbacks.clear();
-
- // Repaint all windows
- for (auto callback : qAsConst(requestUpdateCallbacksCopy))
- callback();
-
- // Pause main loop if no updates were requested. Updates will be
- // restarted again by registerRequestUpdateCallback().
- if (that->m_requestUpdateCallbacks.isEmpty())
- emscripten_pause_main_loop();
-
- that->doMaintainTimers();
- };
- int fps = 0; // update using requestAnimationFrame
- int simulateInfiniteLoop = 1;
- emscripten_set_main_loop_arg(callback, this, fps, simulateInfiniteLoop);
-
- // Note: the above call never returns, not even at app exit
- return false;
-}
-
-void QWasmEventDispatcher::doMaintainTimers()
-{
- Q_D(QWasmEventDispatcher);
-
- // This function schedules native timers in order to wake up to
- // process events and activate Qt timers. This is done using the
- // emscripten_async_call() API which schedules a new timer.
- // There is unfortunately no way to cancel or update a current
- // native timer.
-
- // Schedule a zero-timer to continue processing any pending events.
- extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
- if (!m_hasZeroTimer && (qGlobalPostedEventsCount() || QWindowSystemInterface::windowSystemEventsQueued())) {
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
- that->m_hasZeroTimer = false;
- that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
-
- // Processing events may have posted new events or created new timers
- that->doMaintainTimers();
- };
-
- emscripten_async_call(callback, this, 0);
- m_hasZeroTimer = true;
- return;
- }
-
- auto timespecToNanosec = [](timespec ts) -> uint64_t { return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000); };
-
- // Get current time and time-to-first-Qt-timer. This polls for system
- // time, and we use this time as the current time for the duration of this call.
- timespec toWait;
- bool hasTimers = d->timerList.timerWait(toWait);
- if (!hasTimers)
- return; // no timer needed
-
- uint64_t currentTime = timespecToNanosec(d->timerList.currentTime);
- uint64_t toWaitDuration = timespecToNanosec(toWait);
-
- // The currently scheduled timer target is stored in m_currentTargetTime.
- // We can re-use it if the new target is equivalent or later.
- uint64_t newTargetTime = currentTime + toWaitDuration;
- if (newTargetTime >= m_currentTargetTime)
- return; // existing timer is good
-
- // Schedule a native timer with a callback which processes events (and timers)
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
- that->m_currentTargetTime = std::numeric_limits<uint64_t>::max();
- that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
-
- // Processing events may have posted new events or created new timers
- that->doMaintainTimers();
- };
- emscripten_async_call(callback, this, toWaitDuration);
- m_currentTargetTime = newTargetTime;
-}
-
-void QWasmEventDispatcher::wakeUp()
-{
-#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
- if (!emscripten_is_main_runtime_thread() && m_hasMainLoop) {
-
- // Make two-step async call to mainThreadWakeUp in order to make sure the
- // call is made at a point where the main thread is idle.
- void (*intermediate)(void *) = [](void *eventdispatcher){
- emscripten_async_call(QWasmEventDispatcher::mainThreadWakeUp, eventdispatcher, 0);
- };
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void *)intermediate, this);
- }
-#endif
- QEventDispatcherUNIX::wakeUp();
-}
-
-void QWasmEventDispatcher::mainThreadWakeUp(void *eventDispatcher)
+// Note: All event dispatcher functionality is implemented in QEventDispatcherWasm
+// in QtCore, except for processWindowSystemEvents() below which uses API from QtGui.
+void QWasmEventDispatcher::processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
- emscripten_resume_main_loop(); // Service possible requestUpdate Calls
- static_cast<QWasmEventDispatcher *>(eventDispatcher)->processEvents(QEventLoop::AllEvents);
+ QWindowSystemInterface::sendWindowSystemEvents(flags);
}
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.h b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
index 8420f2cf1a..0ce3e6fce1 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.h
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
@@ -30,35 +30,14 @@
#ifndef QWASMEVENTDISPATCHER_H
#define QWASMEVENTDISPATCHER_H
-#include <QtCore/qhash.h>
-#include <QtCore/qloggingcategory.h>
-#include <QtGui/private/qunixeventdispatcher_qpa_p.h>
+#include <QtCore/private/qeventdispatcher_wasm_p.h>
QT_BEGIN_NAMESPACE
-class QWasmEventDispatcherPrivate;
-
-class QWasmEventDispatcher : public QUnixEventDispatcherQPA
+class QWasmEventDispatcher : public QEventDispatcherWasm
{
- Q_DECLARE_PRIVATE(QWasmEventDispatcher)
-public:
- explicit QWasmEventDispatcher(QObject *parent = nullptr);
- ~QWasmEventDispatcher();
-
- static bool registerRequestUpdateCallback(std::function<void(void)> callback);
- static void maintainTimers();
-
protected:
- bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
- void doMaintainTimers();
- void wakeUp() override;
- static void mainThreadWakeUp(void *eventDispatcher);
-
-private:
- bool m_hasMainLoop = false;
- bool m_hasZeroTimer = false;
- uint64_t m_currentTargetTime = std::numeric_limits<uint64_t>::max();
- QList<std::function<void(void)>> m_requestUpdateCallbacks;
+ void processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index 6d4ead60d5..598b77f541 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -371,7 +371,6 @@ int QWasmEventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mo
{
QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
bool accepted = translator->processMouse(eventType,mouseEvent);
- QWasmEventDispatcher::maintainTimers();
return accepted;
}
@@ -592,7 +591,6 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
bool accepted = QWindowSystemInterface::handleWheelEvent(window2, getTimestamp(), localPoint,
globalPoint, QPoint(), pixelDelta, modifiers);
- QWasmEventDispatcher::maintainTimers();
return static_cast<int>(accepted);
}
@@ -676,8 +674,6 @@ int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent
if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
accepted = QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier);
- QWasmEventDispatcher::maintainTimers();
-
return static_cast<int>(accepted);
}
@@ -873,8 +869,6 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
accepted = false; // continue on to event
}
- QWasmEventDispatcher::maintainTimers();
-
return accepted;
}
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 15dd9057fa..9c5f1e4e74 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -63,6 +63,8 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
QWasmWindow::~QWasmWindow()
{
m_compositor->removeWindow(this);
+ if (m_requestAnimationFrameId > -1)
+ emscripten_cancel_animation_frame(m_requestAnimationFrameId);
}
void QWasmWindow::destroy()
@@ -395,16 +397,14 @@ qreal QWasmWindow::devicePixelRatio() const
void QWasmWindow::requestUpdate()
{
- QPointer<QWindow> windowPointer(window());
- bool registered = QWasmEventDispatcher::registerRequestUpdateCallback([=](){
- if (windowPointer.isNull())
- return;
-
- deliverUpdateRequest();
- });
-
- if (!registered)
- QPlatformWindow::requestUpdate();
+ static auto frame = [](double time, void *context) -> int {
+ Q_UNUSED(time);
+ QWasmWindow *window = static_cast<QWasmWindow *>(context);
+ window->m_requestAnimationFrameId = -1;
+ window->deliverUpdateRequest();
+ return 0;
+ };
+ m_requestAnimationFrameId = emscripten_request_animation_frame(frame, this);
}
bool QWasmWindow::hasTitleBar() const
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 870377ce29..5df6d9dc66 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -122,6 +122,7 @@ protected:
WId m_winid = 0;
bool m_hasTitle = false;
bool m_needsCompositor = false;
+ long m_requestAnimationFrameId = -1;
friend class QWasmCompositor;
friend class QWasmEventTranslator;
bool windowIsPopupType(Qt::WindowFlags flags) const;