7#include <QtCore/qapplicationstatic.h>
10#include <emscripten/val.h>
11#include <emscripten/bind.h>
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42QWasmSuspendResumeControl *QWasmSuspendResumeControl::s_suspendResumeControl =
nullptr;
49 Module.qtSuspendResumeControl = ({
51 asyncifyEnabled:
false,
54 exclusiveEventHandler: 0,
72 function createNamedFunction(name, parent, obj) {
74 [name]: function(...args) {
75 return obj.call(parent, args);
80 function deepShallowClone(obj) {
84 if (!(obj instanceof Event))
88 for (
const key in obj) {
89 if (
typeof obj[key] ===
'function')
90 objCopy[key] = createNamedFunction(obj[key].name, obj, obj[key]);
92 objCopy[key] = obj[key];
95 objCopy[
'isInstanceOfEvent'] =
true;
100 let control = Module.qtSuspendResumeControl;
101 let handler = (arg) => {
104 arg = deepShallowClone(arg);
107 control.pendingEvents.push({
113 if (control.exclusiveEventHandler > 0) {
115 if (index != control.exclusiveEventHandler)
118 const resume = control.resume;
119 control.resume = null;
121 }
else if (control.resume) {
123 const resume = control.resume;
124 control.resume = null;
127 if (control.asyncifyEnabled) {
136 Module.qtSendPendingEvents();
140 control.eventHandlers[index] = handler;
144QWasmSuspendResumeControl::QWasmSuspendResumeControl()
147 Q_ASSERT(emscripten_is_main_runtime_thread());
149 qtSuspendResumeControlClearJs();
150 suspendResumeControlJs().set(
"asyncifyEnabled", qstdweb::haveAsyncify());
151 QWasmSuspendResumeControl::s_suspendResumeControl =
this;
154QWasmSuspendResumeControl::~QWasmSuspendResumeControl()
156 qtSuspendResumeControlClearJs();
157 QWasmSuspendResumeControl::s_suspendResumeControl =
nullptr;
160QWasmSuspendResumeControl *QWasmSuspendResumeControl::get()
162 Q_ASSERT_X(s_suspendResumeControl,
"QWasmSuspendResumeControl",
"Must create a QWasmSuspendResumeControl instance first");
163 return s_suspendResumeControl;
167uint32_t QWasmSuspendResumeControl::registerEventHandler(std::function<
void(val)> handler)
169 static uint32_t i = 0;
171 m_eventHandlers.emplace(i, std::move(handler));
172 qtRegisterEventHandlerJs(i);
177void QWasmSuspendResumeControl::removeEventHandler(uint32_t index)
179 m_eventHandlers.erase(index);
180 suspendResumeControlJs()[
"eventHandlers"].set(index, val::null());
184val QWasmSuspendResumeControl::jsEventHandlerAt(uint32_t index)
186 return suspendResumeControlJs()[
"eventHandlers"][index];
189emscripten::val QWasmSuspendResumeControl::suspendResumeControlJs()
191 return val::module_property(
"qtSuspendResumeControl");
195void QWasmSuspendResumeControl::suspend()
200void QWasmSuspendResumeControl::suspendExclusive(QList<uint32_t> eventHandlerIndices)
202 m_eventFilter = [eventHandlerIndices](
int handler) {
203 return eventHandlerIndices.contains(handler);
206 suspendResumeControlJs().set(
"exclusiveEventHandler", eventHandlerIndices.back());
211int QWasmSuspendResumeControl::sendPendingEvents()
214 Q_ASSERT(emscripten_is_main_runtime_thread());
216 emscripten::val control = suspendResumeControlJs();
217 emscripten::val pendingEvents = control[
"pendingEvents"];
220 for (
int i = 0; i < pendingEvents[
"length"].as<
int>();) {
221 if (!m_eventFilter(pendingEvents[i][
"index"].as<
int>())) {
225 emscripten::val event = pendingEvents[i];
226 pendingEvents.call<
void>(
"splice", i, 1);
228 auto it = m_eventHandlers.find(event[
"index"].as<
int>());
229 if (it != m_eventHandlers.end()) {
230 setCurrentEvent(event[
"arg"]);
231 it->second(currentEvent());
232 setCurrentEvent(emscripten::val::undefined());
238 if (control[
"exclusiveEventHandler"].as<
int>() > 0) {
239 control.set(
"exclusiveEventHandler", 0);
240 m_eventFilter = [](
int) {
return true;};
247 if (QWasmSuspendResumeControl::s_suspendResumeControl)
248 QWasmSuspendResumeControl::s_suspendResumeControl->sendPendingEvents();
258QWasmEventHandler::QWasmEventHandler(emscripten::val element,
const std::string &name, std::function<
void(emscripten::val)> handler)
262 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
263 Q_ASSERT(suspendResume);
264 m_eventHandlerIndex = suspendResume->registerEventHandler(std::move(handler));
265 m_element.call<
void>(
"addEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
268QWasmEventHandler::~QWasmEventHandler()
271 if (m_element.isUndefined())
274 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
275 Q_ASSERT(suspendResume);
276 m_element.call<
void>(
"removeEventListener", m_name, suspendResume->jsEventHandlerAt(m_eventHandlerIndex));
277 suspendResume->removeEventHandler(m_eventHandlerIndex);
280QWasmEventHandler::QWasmEventHandler(QWasmEventHandler&& other)
noexcept
281:m_element(std::move(other.m_element))
282,m_name(std::move(other.m_name))
283,m_eventHandlerIndex(other.m_eventHandlerIndex)
285 other.m_element = emscripten::val();
286 other.m_name = emscripten::val();
287 other.m_eventHandlerIndex = 0;
290QWasmEventHandler& QWasmEventHandler::operator=(QWasmEventHandler&& other)
noexcept
292 m_element = std::move(other.m_element);
293 other.m_element = emscripten::val();
294 m_name = std::move(other.m_name);
295 other.m_name = emscripten::val();
296 m_eventHandlerIndex = other.m_eventHandlerIndex;
297 other.m_eventHandlerIndex = 0;
310 auto wrapper = [handler = std::move(handler),
this](val argument) {
318 m_handlerIndex = m_suspendResume->registerEventHandler(std::move(wrapper));
324 m_suspendResume->removeEventHandler(m_handlerIndex);
331 val jsHandler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
332 using ArgType =
double;
333 ArgType timoutValue =
static_cast<ArgType>(timeout.count());
334 ArgType timerId = val::global(
"window").call<ArgType>(
"setTimeout", jsHandler, timoutValue);
335 m_timerId =
static_cast<int64_t>(std::round(timerId));
340 return m_timerId > 0;
345 val::global(
"window").call<
void>(
"clearTimeout",
double(m_timerId));
355QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler()
357 auto wrapper = [
this](val arg) {
358 handleAnimationFrame(arg.as<
double>());
360 m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper);
363QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler()
365 cancelAnimationFrameRequest();
366 QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex);
370QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance()
372 return s_animationFrameHandler();
376uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback)
378 uint32_t handle = ++m_nextAnimateHandle;
379 m_animateCallbacks[handle] = std::move(callback);
380 ensureAnimationFrameRequested();
385uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback)
387 uint32_t handle = ++m_nextDrawHandle;
388 m_drawCallbacks[handle] = std::move(callback);
389 ensureAnimationFrameRequested();
393void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle)
395 m_animateCallbacks.erase(handle);
396 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
397 cancelAnimationFrameRequest();
400void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle)
402 m_drawCallbacks.erase(handle);
403 if (m_animateCallbacks.empty() && m_drawCallbacks.empty())
404 cancelAnimationFrameRequest();
407void QWasmAnimationFrameMultiHandler::handleAnimationFrame(
double timestamp)
413 auto animateCallbacksCopy = m_animateCallbacks;
414 for (
const auto &pair : animateCallbacksCopy)
415 pair.second(timestamp);
420 auto drawCallbacksCopy = m_drawCallbacks;
421 m_drawCallbacks.clear();
422 for (
const auto &pair : drawCallbacksCopy)
423 pair.second(timestamp);
426 if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty())
427 ensureAnimationFrameRequested();
430void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested()
432 if (m_requestId != -1)
435 using ReturnType =
double;
436 val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex);
437 m_requestId = int64_t(val::global(
"window").call<ReturnType>(
"requestAnimationFrame", handler));
440void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest()
442 if (m_requestId == -1)
445 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()