8#include <QtCore/qapplicationstatic.h>
10#include <emscripten.h>
11#include <emscripten/val.h>
12#include <emscripten/bind.h>
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
43QWasmSuspendResumeControl *QWasmSuspendResumeControl::s_suspendResumeControl =
nullptr;
50 Module.qtSuspendResumeControl = ({
52 asyncifyEnabled:
false,
55 exclusiveEventHandler: 0,
73 function createNamedFunction(name, parent, obj) {
75 [name]: function(...args) {
76 return obj.call(parent, args);
81 function deepShallowClone(obj) {
85 if (!(obj instanceof Event))
89 for (
const key in obj) {
90 if (
typeof obj[key] ===
'function')
91 objCopy[key] = createNamedFunction(obj[key].name, obj, obj[key]);
93 objCopy[key] = obj[key];
96 objCopy[
'isInstanceOfEvent'] =
true;
101 let control = Module.qtSuspendResumeControl;
102 let handler = (arg) => {
105 arg = deepShallowClone(arg);
108 control.pendingEvents.push({
114 if (control.exclusiveEventHandler > 0) {
116 if (index != control.exclusiveEventHandler)
119 const resume = control.resume;
120 control.resume = null;
122 }
else if (control.resume) {
124 const resume = control.resume;
125 control.resume = null;
128 if (control.asyncifyEnabled) {
137 Module.qtSendPendingEvents();
141 control.eventHandlers[index] = handler;
145QWasmSuspendResumeControl::QWasmSuspendResumeControl()
148 Q_ASSERT(emscripten_is_main_runtime_thread());
150 qtSuspendResumeControlClearJs();
151 suspendResumeControlJs().set(
"asyncifyEnabled", qstdweb::haveAsyncify());
152 QWasmSuspendResumeControl::s_suspendResumeControl =
this;
155QWasmSuspendResumeControl::~QWasmSuspendResumeControl()
157 qtSuspendResumeControlClearJs();
158 QWasmSuspendResumeControl::s_suspendResumeControl =
nullptr;
161QWasmSuspendResumeControl *QWasmSuspendResumeControl::get()
163 Q_ASSERT_X(s_suspendResumeControl,
"QWasmSuspendResumeControl",
"Must create a QWasmSuspendResumeControl instance first");
164 return s_suspendResumeControl;
168uint32_t QWasmSuspendResumeControl::registerEventHandler(std::function<
void(val)> handler)
170 static uint32_t i = 0;
172 m_eventHandlers.emplace(i, std::move(handler));
173 qtRegisterEventHandlerJs(i);
178void QWasmSuspendResumeControl::removeEventHandler(uint32_t index)
180 m_eventHandlers.erase(index);
181 suspendResumeControlJs()[
"eventHandlers"].set(index, val::null());
185val QWasmSuspendResumeControl::jsEventHandlerAt(uint32_t index)
187 return suspendResumeControlJs()[
"eventHandlers"][index];
190emscripten::val QWasmSuspendResumeControl::suspendResumeControlJs()
192 return val::module_property(
"qtSuspendResumeControl");
196void QWasmSuspendResumeControl::suspend()
201void QWasmSuspendResumeControl::suspendExclusive(QList<uint32_t> eventHandlerIndices)
203 m_eventFilter = [eventHandlerIndices](
int handler) {
204 return eventHandlerIndices.contains(handler);
207 suspendResumeControlJs().set(
"exclusiveEventHandler", eventHandlerIndices.back());
212int QWasmSuspendResumeControl::sendPendingEvents()
215 Q_ASSERT(emscripten_is_main_runtime_thread());
217 emscripten::val control = suspendResumeControlJs();
218 emscripten::val pendingEvents = control[
"pendingEvents"];
221 for (
int i = 0; i < pendingEvents[
"length"].as<
int>();) {
222 if (!m_eventFilter(pendingEvents[i][
"index"].as<
int>())) {
226 emscripten::val event = pendingEvents[i];
227 pendingEvents.call<
void>(
"splice", i, 1);
229 auto it = m_eventHandlers.find(event[
"index"].as<
int>());
230 if (it != m_eventHandlers.end()) {
231 setCurrentEvent(event[
"arg"]);
232 it->second(currentEvent());
233 setCurrentEvent(emscripten::val::undefined());
239 if (control[
"exclusiveEventHandler"].as<
int>() > 0) {
240 control.set(
"exclusiveEventHandler", 0);
241 m_eventFilter = [](
int) {
return true;};
248 if (QWasmSuspendResumeControl::s_suspendResumeControl)
249 QWasmSuspendResumeControl::s_suspendResumeControl->sendPendingEvents();
259QWasmEventHandler::QWasmEventHandler(emscripten::val element,
const std::string &name, std::function<
void(emscripten::val)> handler)
263 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
264 Q_ASSERT(suspendResume);
265 m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
266 m_element.call<
void>(
"addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
269QWasmEventHandler::~QWasmEventHandler()
272 if (m_element.isUndefined())
275 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
276 Q_ASSERT(suspendResume);
277 m_element.call<
void>(
"removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
278 suspendResume->removeEventHandler(m_eventHandlerIndex);
281QWasmEventHandler::QWasmEventHandler(QWasmEventHandler&& other)
noexcept
282:m_element(std::move(other.m_element))
283,m_name(std::move(other.m_name))
284,m_eventHandlerIndex(other.m_eventHandlerIndex)
286 other.m_element = emscripten::val();
287 other.m_name = emscripten::val();
288 other.m_eventHandlerIndex = 0;
291QWasmEventHandler& QWasmEventHandler::operator=(QWasmEventHandler&& other)
noexcept
293 m_element = std::move(other.m_element);
294 other.m_element = emscripten::val();
295 m_name = std::move(other.m_name);
296 other.m_name = emscripten::val();
297 m_eventHandlerIndex = other.m_eventHandlerIndex;
298 other.m_eventHandlerIndex = 0;
311 auto wrapper = [handler = std::move(handler),
this](val argument) {
319 m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
325 m_suspendResume->removeEventHandler(m_handlerIndex);
332 val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
333 using ArgType =
double;
334 ArgType timoutValue =
static_cast<ArgType>(timeout.count());
335 ArgType timerId = val::global(
"window").call<ArgType>(
"setTimeout", jsHandler, timoutValue);
336 m_timerId =
static_cast<int64_t>(std::round(timerId));
341 return m_timerId > 0;
346 val::global(
"window").call<
void>(
"clearTimeout",
double(m_timerId));
356QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler()
358 auto wrapper = [
this](val arg) {
359 handleAnimationFrame(arg.as<
double>());
361 m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper);
364QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler()
366 cancelAnimationFrameRequest();
367 QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex);
371QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance()
373 return s_animationFrameHandler();
377uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback)
379 uint32_t handle = ++m_nextAnimateHandle;
380 m_animateCallbacks[handle] = std::move(callback);
381 ensureAnimationFrameRequested();
386uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback)
388 uint32_t handle = ++m_nextDrawHandle;
389 m_drawCallbacks[handle] = std::move(callback);
390 ensureAnimationFrameRequested();
394void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle)
396 m_animateCallbacks.erase(handle);
397 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
398 cancelAnimationFrameRequest();
401void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle)
403 m_drawCallbacks.erase(handle);
404 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
405 cancelAnimationFrameRequest();
408void QWasmAnimationFrameMultiHandler::handleAnimationFrame(
double timestamp)
414 auto animateCallbacksCopy = m_animateCallbacks;
415 for (
const auto &pair : animateCallbacksCopy)
416 pair.second(timestamp);
421 auto drawCallbacksCopy = m_drawCallbacks;
422 m_drawCallbacks.clear();
423 for (
const auto &pair : drawCallbacksCopy)
424 pair.second(timestamp);
427 if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty())
428 ensureAnimationFrameRequested();
431void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested()
433 if (m_requestId != -1)
436 using ReturnType =
double;
437 val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
438 m_requestId = int64_t(val::global(
"window").call<ReturnType>(
"requestAnimationFrame", handler));
441void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest()
443 if (m_requestId == -1)
446 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()