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,
74 function createNamedFunction(name, parent, obj) {
76 [name]: function(...args) {
77 return obj.call(parent, args);
82 function deepShallowClone(obj) {
86 if (!(obj instanceof Event))
90 for (
const key in obj) {
91 if (
typeof obj[key] ===
'function')
92 objCopy[key] = createNamedFunction(obj[key].name, obj, obj[key]);
94 objCopy[key] = obj[key];
97 objCopy[
'isInstanceOfEvent'] =
true;
102 let control = Module.qtSuspendResumeControl;
103 let handler = (arg) => {
106 arg = deepShallowClone(arg);
109 control.pendingEvents.push({
115 if (control.exclusiveEventHandler > 0) {
118 if (index != control.exclusiveEventHandler)
121 const resume = control.resume;
122 control.resume = null;
124 }
else if (control.resume) {
126 const resume = control.resume;
127 control.resume = null;
130 if (control.asyncifyEnabled) {
139 Module.qtSendPendingEvents();
143 control.eventHandlers[index] = handler;
147QWasmSuspendResumeControl::QWasmSuspendResumeControl()
150 Q_ASSERT(emscripten_is_main_runtime_thread());
152 qtSuspendResumeControlClearJs();
153 suspendResumeControlJs().set(
"asyncifyEnabled", qstdweb::haveAsyncify());
156QWasmSuspendResumeControl::~QWasmSuspendResumeControl()
158 if (!m_eventHandlers.empty())
159 qWarning() <<
"QWasmSuspendResumeControl::~QWasmSuspendResumeControl - still remaining " << m_eventHandlers.size() <<
" handlers";
160 qtSuspendResumeControlClearJs();
163QWasmSuspendResumeControl *QWasmSuspendResumeControl::get()
165 if (!s_suspendResumeControl)
166 qFatal(
"QWasmSuspendResumeControl -- Object not created/destroyed");
168 return s_suspendResumeControl;
172uint32_t QWasmSuspendResumeControl::registerEventHandler(std::function<
void(val)> handler)
174 static uint32_t i = 0;
176 m_eventHandlers.emplace(i, std::move(handler));
177 qtRegisterEventHandlerJs(i);
182void QWasmSuspendResumeControl::removeEventHandler(uint32_t index)
184 m_eventHandlers.erase(index);
185 suspendResumeControlJs()[
"eventHandlers"].set(index, val::null());
189val QWasmSuspendResumeControl::jsEventHandlerAt(uint32_t index)
191 return suspendResumeControlJs()[
"eventHandlers"][index];
194emscripten::val QWasmSuspendResumeControl::suspendResumeControlJs()
196 return val::module_property(
"qtSuspendResumeControl");
200void QWasmSuspendResumeControl::suspend()
202 if (!qstdweb::canBlockCallingThread()) {
203 qFatal(
"Suspending the main thread requires asyncify or JSPI; "
204 "see the Qt for WebAssembly documentation for how to enable.");
209void QWasmSuspendResumeControl::suspendExclusive(QList<uint32_t> eventHandlerIndices)
211 if (!qstdweb::canBlockCallingThread()) {
212 qFatal(
"Suspending the main thread requires asyncify or JSPI; "
213 "see the Qt for WebAssembly documentation for how to enable.");
216 m_eventFilter = [eventHandlerIndices](
int handler) {
217 return eventHandlerIndices.contains(handler);
220 suspendResumeControlJs().set(
"exclusiveEventHandler", eventHandlerIndices.back());
225int QWasmSuspendResumeControl::sendPendingEvents()
228 Q_ASSERT(emscripten_is_main_runtime_thread());
230 emscripten::val control = suspendResumeControlJs();
231 emscripten::val pendingEvents = control[
"pendingEvents"];
234 for (
int i = 0; i < pendingEvents[
"length"].as<
int>();) {
235 if (!m_eventFilter(pendingEvents[i][
"index"].as<
int>())) {
239 emscripten::val event = pendingEvents[i];
240 pendingEvents.call<
void>(
"splice", i, 1);
242 auto it = m_eventHandlers.find(event[
"index"].as<
int>());
243 if (it != m_eventHandlers.end()) {
244 setCurrentEvent(event[
"arg"]);
245 it->second(currentEvent());
246 setCurrentEvent(emscripten::val::undefined());
252 if (control[
"exclusiveEventHandler"].as<
int>() > 0) {
253 control.set(
"exclusiveEventHandler", 0);
254 m_eventFilter = [](
int) {
return true;};
261 if (s_suspendResumeControl)
262 s_suspendResumeControl->sendPendingEvents();
272QWasmEventHandler::QWasmEventHandler(emscripten::val element,
const std::string &name, std::function<
void(emscripten::val)> handler)
276 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
277 m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
278 m_element.call<
void>(
"addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
281QWasmEventHandler::~QWasmEventHandler()
284 if (m_element.isUndefined())
287 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
288 m_element.call<
void>(
"removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
289 suspendResume->removeEventHandler(m_eventHandlerIndex);
292QWasmEventHandler::QWasmEventHandler(QWasmEventHandler&& other)
noexcept
293:m_element(std::move(other.m_element))
294,m_name(std::move(other.m_name))
295,m_eventHandlerIndex(other.m_eventHandlerIndex)
297 other.m_element = emscripten::val();
298 other.m_name = emscripten::val();
299 other.m_eventHandlerIndex = 0;
302QWasmEventHandler& QWasmEventHandler::operator=(QWasmEventHandler&& other)
noexcept
304 m_element = std::move(other.m_element);
305 other.m_element = emscripten::val();
306 m_name = std::move(other.m_name);
307 other.m_name = emscripten::val();
308 m_eventHandlerIndex = other.m_eventHandlerIndex;
309 other.m_eventHandlerIndex = 0;
322 auto wrapper = [handler = std::move(handler),
this](val argument) {
330 m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
337 m_suspendResume->removeEventHandler(m_handlerIndex);
342 Q_ASSERT(m_suspendResume == QWasmSuspendResumeControl::get());
345 val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
346 using ArgType =
double;
347 ArgType timoutValue =
static_cast<ArgType>(timeout.count());
348 ArgType timerId = val::global(
"window").call<ArgType>(
"setTimeout", jsHandler, timoutValue);
349 m_timerId =
static_cast<int64_t>(std::round(timerId));
354 return m_timerId > 0;
359 val::global(
"window").call<
void>(
"clearTimeout",
double(m_timerId));
369QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler()
371 auto wrapper = [
this](val arg) {
372 handleAnimationFrame(arg.as<
double>());
374 m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper);
377QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler()
379 cancelAnimationFrameRequest();
380 QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex);
384QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance()
386 return s_animationFrameHandler();
390uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback)
392 uint32_t handle = ++m_nextAnimateHandle;
393 m_animateCallbacks[handle] = std::move(callback);
394 ensureAnimationFrameRequested();
399uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback)
401 uint32_t handle = ++m_nextDrawHandle;
402 m_drawCallbacks[handle] = std::move(callback);
403 ensureAnimationFrameRequested();
407void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle)
409 m_animateCallbacks.erase(handle);
410 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
411 cancelAnimationFrameRequest();
414void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle)
416 m_drawCallbacks.erase(handle);
417 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
418 cancelAnimationFrameRequest();
421void QWasmAnimationFrameMultiHandler::handleAnimationFrame(
double timestamp)
427 auto animateCallbacksCopy = m_animateCallbacks;
428 for (
const auto &pair : animateCallbacksCopy)
429 pair.second(timestamp);
434 auto drawCallbacksCopy = m_drawCallbacks;
435 m_drawCallbacks.clear();
436 for (
const auto &pair : drawCallbacksCopy)
437 pair.second(timestamp);
440 if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty())
441 ensureAnimationFrameRequested();
444void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested()
446 if (m_requestId != -1)
449 using ReturnType =
double;
450 val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
451 m_requestId = int64_t(val::global(
"window").call<ReturnType>(
"requestAnimationFrame", handler));
454void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest()
456 if (m_requestId == -1)
459 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()