8#include <QtCore/qapplicationstatic.h>
9#include <QtCore/qdebug.h>
11#include <emscripten.h>
12#include <emscripten/val.h>
13#include <emscripten/bind.h>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
51 Module.qtSuspendResumeControl = ({
53 asyncifyEnabled:
false,
56 exclusiveEventHandler: 0,
76 function snapshotEvent(event) {
79 if (!(event instanceof Event))
83 const copy = { isInstanceOfEvent:
true };
84 for (
const key in event) {
85 const value = event[key];
86 copy[key] =
typeof value ===
'function' ? value.bind(event) : value;
93 let control = Module.qtSuspendResumeControl;
94 let handler = (arg) => {
99 arg = snapshotEvent(arg);
102 control.pendingEvents.push({
108 if (control.exclusiveEventHandler > 0) {
111 if (index != control.exclusiveEventHandler)
114 const resume = control.resume;
115 control.resume = null;
117 }
else if (control.resume) {
119 const resume = control.resume;
120 control.resume = null;
123 if (control.asyncifyEnabled) {
132 Module.qtSendPendingEvents();
136 control.eventHandlers[index] = handler;
140QWasmSuspendResumeControl::QWasmSuspendResumeControl()
143 Q_ASSERT(emscripten_is_main_runtime_thread());
145 qtSuspendResumeControlClearJs();
146 suspendResumeControlJs().set(
"asyncifyEnabled", qstdweb::haveAsyncify());
149QWasmSuspendResumeControl::~QWasmSuspendResumeControl()
151 if (!m_eventHandlers.empty())
152 qWarning() <<
"QWasmSuspendResumeControl::~QWasmSuspendResumeControl - still remaining " << m_eventHandlers.size() <<
" handlers";
153 qtSuspendResumeControlClearJs();
156QWasmSuspendResumeControl *QWasmSuspendResumeControl::get()
158 if (!s_suspendResumeControl)
159 qFatal(
"QWasmSuspendResumeControl -- Object not created/destroyed");
161 return s_suspendResumeControl;
165uint32_t QWasmSuspendResumeControl::registerEventHandler(std::function<
void(val)> handler)
167 static uint32_t i = 0;
169 m_eventHandlers.emplace(i, std::move(handler));
170 qtRegisterEventHandlerJs(i);
175void QWasmSuspendResumeControl::removeEventHandler(uint32_t index)
177 m_eventHandlers.erase(index);
178 suspendResumeControlJs()[
"eventHandlers"].set(index, val::null());
182val QWasmSuspendResumeControl::jsEventHandlerAt(uint32_t index)
184 return suspendResumeControlJs()[
"eventHandlers"][index];
187emscripten::val QWasmSuspendResumeControl::suspendResumeControlJs()
189 return val::module_property(
"qtSuspendResumeControl");
193void QWasmSuspendResumeControl::suspend()
195 if (!qstdweb::canBlockCallingThread()) {
196 qFatal(
"Suspending the main thread requires asyncify or JSPI; "
197 "see the Qt for WebAssembly documentation for how to enable.");
202void QWasmSuspendResumeControl::suspendExclusive(QList<uint32_t> eventHandlerIndices)
204 if (!qstdweb::canBlockCallingThread()) {
205 qFatal(
"Suspending the main thread requires asyncify or JSPI; "
206 "see the Qt for WebAssembly documentation for how to enable.");
209 m_eventFilter = [eventHandlerIndices](
int handler) {
210 return eventHandlerIndices.contains(handler);
213 suspendResumeControlJs().set(
"exclusiveEventHandler", eventHandlerIndices.back());
218int QWasmSuspendResumeControl::sendPendingEvents()
221 Q_ASSERT(emscripten_is_main_runtime_thread());
223 emscripten::val control = suspendResumeControlJs();
224 emscripten::val pendingEvents = control[
"pendingEvents"];
227 for (
int i = 0; i < pendingEvents[
"length"].as<
int>();) {
228 if (!m_eventFilter(pendingEvents[i][
"index"].as<
int>())) {
232 emscripten::val event = pendingEvents[i];
233 pendingEvents.call<
void>(
"splice", i, 1);
235 auto it = m_eventHandlers.find(event[
"index"].as<
int>());
236 if (it != m_eventHandlers.end()) {
237 setCurrentEvent(event[
"arg"]);
238 it->second(currentEvent());
239 setCurrentEvent(emscripten::val::undefined());
245 if (control[
"exclusiveEventHandler"].as<
int>() > 0) {
246 control.set(
"exclusiveEventHandler", 0);
247 m_eventFilter = [](
int) {
return true;};
254 if (s_suspendResumeControl)
255 s_suspendResumeControl->sendPendingEvents();
265QWasmEventHandler::QWasmEventHandler(emscripten::val element,
const std::string &name, std::function<
void(emscripten::val)> handler)
269 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
270 m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
271 m_element.call<
void>(
"addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
274QWasmEventHandler::~QWasmEventHandler()
277 if (m_element.isUndefined())
280 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
281 m_element.call<
void>(
"removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
282 suspendResume->removeEventHandler(m_eventHandlerIndex);
285QWasmEventHandler::QWasmEventHandler(QWasmEventHandler&& other)
noexcept
286:m_element(std::move(other.m_element))
287,m_name(std::move(other.m_name))
288,m_eventHandlerIndex(other.m_eventHandlerIndex)
290 other.m_element = emscripten::val();
291 other.m_name = emscripten::val();
292 other.m_eventHandlerIndex = 0;
295QWasmEventHandler& QWasmEventHandler::operator=(QWasmEventHandler&& other)
noexcept
297 m_element = std::move(other.m_element);
298 other.m_element = emscripten::val();
299 m_name = std::move(other.m_name);
300 other.m_name = emscripten::val();
301 m_eventHandlerIndex = other.m_eventHandlerIndex;
302 other.m_eventHandlerIndex = 0;
315 auto wrapper = [handler = std::move(handler),
this](val argument) {
323 m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
330 m_suspendResume->removeEventHandler(m_handlerIndex);
335 Q_ASSERT(m_suspendResume == QWasmSuspendResumeControl::get());
338 val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
339 using ArgType =
double;
340 ArgType timoutValue =
static_cast<ArgType>(timeout.count());
341 ArgType timerId = val::global(
"window").call<ArgType>(
"setTimeout", jsHandler, timoutValue);
342 m_timerId =
static_cast<int64_t>(std::round(timerId));
347 return m_timerId > 0;
352 val::global(
"window").call<
void>(
"clearTimeout",
double(m_timerId));
362QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler()
364 auto wrapper = [
this](val arg) {
365 handleAnimationFrame(arg.as<
double>());
367 m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper);
370QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler()
372 cancelAnimationFrameRequest();
373 QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex);
377QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance()
379 return s_animationFrameHandler();
383uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback)
385 uint32_t handle = ++m_nextAnimateHandle;
386 m_animateCallbacks[handle] = std::move(callback);
387 ensureAnimationFrameRequested();
392uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback)
394 uint32_t handle = ++m_nextDrawHandle;
395 m_drawCallbacks[handle] = std::move(callback);
396 ensureAnimationFrameRequested();
400void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle)
402 m_animateCallbacks.erase(handle);
403 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
404 cancelAnimationFrameRequest();
407void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle)
409 m_drawCallbacks.erase(handle);
410 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
411 cancelAnimationFrameRequest();
414void QWasmAnimationFrameMultiHandler::handleAnimationFrame(
double timestamp)
420 auto animateCallbacksCopy = m_animateCallbacks;
421 for (
const auto &pair : animateCallbacksCopy)
422 pair.second(timestamp);
427 auto drawCallbacksCopy = m_drawCallbacks;
428 m_drawCallbacks.clear();
429 for (
const auto &pair : drawCallbacksCopy)
430 pair.second(timestamp);
433 if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty())
434 ensureAnimationFrameRequested();
437void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested()
439 if (m_requestId != -1)
442 using ReturnType =
double;
443 val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
444 m_requestId = int64_t(val::global(
"window").call<ReturnType>(
"requestAnimationFrame", handler));
447void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest()
449 if (m_requestId == -1)
452 val::global(
"window").call<
void>(
"cancelAnimationFrame",
double(m_requestId));
void setTimeout(std::chrono::milliseconds timeout)
QWasmTimer(QWasmSuspendResumeControl *suspendResume, std::function< void()> handler)
Q_GLOBAL_STATIC(DefaultRoleNames, qDefaultRoleNames, { { Qt::DisplayRole, "display" }, { Qt::DecorationRole, "decoration" }, { Qt::EditRole, "edit" }, { Qt::ToolTipRole, "toolTip" }, { Qt::StatusTipRole, "statusTip" }, { Qt::WhatsThisRole, "whatsThis" }, }) const QHash< int
#define QT_WASM_EMSCRIPTEN_ASYNC
EMSCRIPTEN_BINDINGS(qtSuspendResumeControl)
EM_ASYNC_JS(void, qtSuspendJs,(), { return new Promise(resolve=> { Module.qtSuspendResumeControl.resume=resolve;});})
void qtSuspendResumeControlClearJs()
void qtRegisterEventHandlerJs(int index)
void qtSendPendingEvents()