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()
205void QWasmSuspendResumeControl::suspendExclusive(QList<uint32_t> eventHandlerIndices)
207 m_eventFilter = [eventHandlerIndices](
int handler) {
208 return eventHandlerIndices.contains(handler);
211 suspendResumeControlJs().set(
"exclusiveEventHandler", eventHandlerIndices.back());
216int QWasmSuspendResumeControl::sendPendingEvents()
219 Q_ASSERT(emscripten_is_main_runtime_thread());
221 emscripten::val control = suspendResumeControlJs();
222 emscripten::val pendingEvents = control[
"pendingEvents"];
225 for (
int i = 0; i < pendingEvents[
"length"].as<
int>();) {
226 if (!m_eventFilter(pendingEvents[i][
"index"].as<
int>())) {
230 emscripten::val event = pendingEvents[i];
231 pendingEvents.call<
void>(
"splice", i, 1);
233 auto it = m_eventHandlers.find(event[
"index"].as<
int>());
234 if (it != m_eventHandlers.end()) {
235 setCurrentEvent(event[
"arg"]);
236 it->second(currentEvent());
237 setCurrentEvent(emscripten::val::undefined());
243 if (control[
"exclusiveEventHandler"].as<
int>() > 0) {
244 control.set(
"exclusiveEventHandler", 0);
245 m_eventFilter = [](
int) {
return true;};
252 if (s_suspendResumeControl)
253 s_suspendResumeControl->sendPendingEvents();
263QWasmEventHandler::QWasmEventHandler(emscripten::val element,
const std::string &name, std::function<
void(emscripten::val)> handler)
267 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
268 m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
269 m_element.call<
void>(
"addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
272QWasmEventHandler::~QWasmEventHandler()
275 if (m_element.isUndefined())
278 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
279 m_element.call<
void>(
"removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
280 suspendResume->removeEventHandler(m_eventHandlerIndex);
283QWasmEventHandler::QWasmEventHandler(QWasmEventHandler&& other)
noexcept
284:m_element(std::move(other.m_element))
285,m_name(std::move(other.m_name))
286,m_eventHandlerIndex(other.m_eventHandlerIndex)
288 other.m_element = emscripten::val();
289 other.m_name = emscripten::val();
290 other.m_eventHandlerIndex = 0;
293QWasmEventHandler& QWasmEventHandler::operator=(QWasmEventHandler&& other)
noexcept
295 m_element = std::move(other.m_element);
296 other.m_element = emscripten::val();
297 m_name = std::move(other.m_name);
298 other.m_name = emscripten::val();
299 m_eventHandlerIndex = other.m_eventHandlerIndex;
300 other.m_eventHandlerIndex = 0;
313 auto wrapper = [handler = std::move(handler),
this](val argument) {
321 m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
328 m_suspendResume->removeEventHandler(m_handlerIndex);
333 Q_ASSERT(m_suspendResume == QWasmSuspendResumeControl::get());
336 val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
337 using ArgType =
double;
338 ArgType timoutValue =
static_cast<ArgType>(timeout.count());
339 ArgType timerId = val::global(
"window").call<ArgType>(
"setTimeout", jsHandler, timoutValue);
340 m_timerId =
static_cast<int64_t>(std::round(timerId));
345 return m_timerId > 0;
350 val::global(
"window").call<
void>(
"clearTimeout",
double(m_timerId));
360QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler()
362 auto wrapper = [
this](val arg) {
363 handleAnimationFrame(arg.as<
double>());
365 m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper);
368QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler()
370 cancelAnimationFrameRequest();
371 QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex);
375QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance()
377 return s_animationFrameHandler();
381uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback)
383 uint32_t handle = ++m_nextAnimateHandle;
384 m_animateCallbacks[handle] = std::move(callback);
385 ensureAnimationFrameRequested();
390uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback)
392 uint32_t handle = ++m_nextDrawHandle;
393 m_drawCallbacks[handle] = std::move(callback);
394 ensureAnimationFrameRequested();
398void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle)
400 m_animateCallbacks.erase(handle);
401 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
402 cancelAnimationFrameRequest();
405void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle)
407 m_drawCallbacks.erase(handle);
408 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
409 cancelAnimationFrameRequest();
412void QWasmAnimationFrameMultiHandler::handleAnimationFrame(
double timestamp)
418 auto animateCallbacksCopy = m_animateCallbacks;
419 for (
const auto &pair : animateCallbacksCopy)
420 pair.second(timestamp);
425 auto drawCallbacksCopy = m_drawCallbacks;
426 m_drawCallbacks.clear();
427 for (
const auto &pair : drawCallbacksCopy)
428 pair.second(timestamp);
431 if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty())
432 ensureAnimationFrameRequested();
435void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested()
437 if (m_requestId != -1)
440 using ReturnType =
double;
441 val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
442 m_requestId = int64_t(val::global(
"window").call<ReturnType>(
"requestAnimationFrame", handler));
445void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest()
447 if (m_requestId == -1)
450 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()