diff options
author | Morten Johan Sørvig <morten.sorvig@qt.io> | 2021-09-12 20:37:42 +0200 |
---|---|---|
committer | Morten Johan Sørvig <morten.sorvig@qt.io> | 2021-12-23 11:21:57 +0100 |
commit | 23964ce05f37fda4594c7eb33162e9bc1be962fc (patch) | |
tree | 987dc297304b3f2788219ad9124c8ff88783d04c /src | |
parent | e98f5de6e1a19e06cc9e94ea01ca27d9a65f8d70 (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.cpp | 194 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmeventdispatcher.h | 27 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmeventtranslator.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 20 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.h | 1 |
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; |