wasm: add native event and timer helpers

These build on QWasmSuspendResumeControl and provide
support for registering C++ event handlers and native
timer callbacks.

Change-Id: I8370f91e42b3992ff7864e847efb9c225010e718
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
This commit is contained in:
Morten Sørvig 2024-11-29 17:22:06 +01:00
parent 3c72f99770
commit de2a29a1fe
2 changed files with 102 additions and 0 deletions

View File

@ -164,3 +164,71 @@ void qtSendPendingEvents()
EMSCRIPTEN_BINDINGS(qtSuspendResumeControl) {
emscripten::function("qtSendPendingEvents", qtSendPendingEvents QT_WASM_EMSCRIPTEN_ASYNC);
}
//
// The EventCallback class registers a callback function for an event on an html element.
//
QWasmEventHandler::QWasmEventHandler(emscripten::val element, const std::string &name, std::function<void(emscripten::val)> handler)
:m_element(element)
,m_name(name)
{
QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
Q_ASSERT(suspendResume); // must construct the event dispatcher or platform integration first
m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
m_element.call<void>("addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
}
QWasmEventHandler::~QWasmEventHandler()
{
QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
Q_ASSERT(suspendResume);
m_element.call<void>("removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
suspendResume->removeEventHandler(m_eventHandlerIndex);
}
//
// The QWasmTimer class creates a native single-shot timer. The event handler is provided in the
// constructor and can be reused: each call setTimeout() sets a new timeout, though with the
// limitiation that there can be only one timeout at a time. (Setting a new timer clears the
// previous one).
//
QWasmTimer::QWasmTimer(QWasmSuspendResumeControl *suspendResume, std::function<void()> handler)
:m_suspendResume(suspendResume)
{
auto wrapper = [handler = std::move(handler), this](val argument) {
Q_UNUSED(argument); // no argument for timers
Q_ASSERT(m_timerId);
m_timerId = 0;
handler();
};
m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
}
QWasmTimer::~QWasmTimer()
{
clearTimeout();
m_suspendResume->removeEventHandler(m_handlerIndex);
}
void QWasmTimer::setTimeout(std::chrono::milliseconds timeout)
{
if (hasTimeout())
clearTimeout();
val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
using ArgType = double; // emscripten::val::call() does not support int64_t
ArgType timoutValue = static_cast<ArgType>(timeout.count());
ArgType timerId = val::global("window").call<ArgType>("setTimeout", jsHandler, timoutValue);
m_timerId = static_cast<int64_t>(std::round(timerId));
}
bool QWasmTimer::hasTimeout()
{
return m_timerId > 0;
}
void QWasmTimer::clearTimeout()
{
val::global("window").call<void>("clearTimeout", double(m_timerId));
m_timerId = 0;
}

View File

@ -8,6 +8,7 @@
#include <emscripten/val.h>
#include <map>
#include <functional>
#include <chrono>
//
// W A R N I N G
@ -46,4 +47,37 @@ private:
std::map<int, std::function<void(emscripten::val)>> m_eventHandlers;
};
class Q_CORE_EXPORT QWasmEventHandler
{
public:
QWasmEventHandler() = default;
~QWasmEventHandler();
QWasmEventHandler(QWasmEventHandler const&) = delete;
QWasmEventHandler& operator=(QWasmEventHandler const&) = delete;
QWasmEventHandler(emscripten::val element, const std::string &name,
std::function<void(emscripten::val)> fn);
private:
emscripten::val m_element;
emscripten::val m_name;
uint32_t m_eventHandlerIndex = 0;
};
class QWasmTimer
{
public:
QWasmTimer(QWasmSuspendResumeControl* suspendResume, std::function<void()> handler);
~QWasmTimer();
QWasmTimer(QWasmTimer const&) = delete;
QWasmTimer& operator=(QWasmTimer const&) = delete;
void setTimeout(std::chrono::milliseconds timeout);
bool hasTimeout();
void clearTimeout();
private:
QWasmSuspendResumeControl *m_suspendResume;
uint32_t m_handlerIndex;
uint64_t m_timerId = 0;
};
#endif