Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwasmintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
8#include "qwasmtheme.h"
9#if QT_CONFIG(clipboard)
10#include "qwasmclipboard.h"
11#endif
13#include "qwasmservices.h"
15#include "qwasmplatform.h"
16#include "qwasmwindow.h"
19#if QT_CONFIG(draganddrop)
20#include "qwasmdrag.h"
21#endif
22
23#include <qpa/qplatformwindow.h>
24#include <QtGui/qscreen.h>
25#include <qpa/qwindowsysteminterface.h>
26#include <QtCore/qcoreapplication.h>
27#include <qpa/qplatforminputcontextfactory_p.h>
28#include <qpa/qwindowsysteminterface_p.h>
29#include "private/qwasmsuspendresumecontrol_p.h"
30
31#include <emscripten/bind.h>
32#include <emscripten/val.h>
33
34// this is where EGL headers are pulled in, make sure it is last
35#include "qwasmscreen.h"
36#if QT_CONFIG(draganddrop)
37#include <private/qsimpledrag_p.h>
38#endif
39
41
42extern void qt_set_sequence_auto_mnemonic(bool);
43
44using namespace emscripten;
45
46using namespace Qt::StringLiterals;
47
48static void setContainerElements(emscripten::val elementArray)
49{
51}
52
57
62
67
72
73static void resizeAllScreens(emscripten::val event)
74{
75 Q_UNUSED(event);
77}
78
83
85{
86 function("qtSetContainerElements", &setContainerElements);
87 function("qtAddContainerElement", &addContainerElement);
88 function("qtRemoveContainerElement", &removeContainerElement);
89 function("qtResizeContainerElement", &resizeContainerElement);
90 function("qtUpdateDpi", &qtUpdateDpi);
91 function("qtResizeAllScreens", &resizeAllScreens);
92 function("qtLoadLocalFontFamilies", &loadLocalFontFamilies);
93}
94
96
97QWasmIntegration::QWasmIntegration()
98 : m_suspendResume(std::make_shared<QWasmSuspendResumeControl>()) // create early in order to register event handlers at startup
99 , m_fontDb(nullptr)
100 , m_desktopServices(nullptr)
101#if QT_CONFIG(clipboard)
102 , m_clipboard(new QWasmClipboard)
103#endif
104#if QT_CONFIG(accessibility)
105 , m_accessibility(new QWasmAccessibility)
106#endif
107{
108 s_instance = this;
109
110 if (platform() == Platform::MacOS)
112
113 touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>();
114 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
115
116 // Create screens for container elements. Each container element will ultimately become a
117 // div element. Qt historically supported supplying canvas for screen elements - these elements
118 // will be transformed into divs and warnings about deprecation will be printed. See
119 // QWasmScreen ctor.
120 emscripten::val filtered = emscripten::val::array();
121 emscripten::val qtContainerElements = val::module_property("qtContainerElements");
122 if (qtContainerElements.isArray()) {
123 for (int i = 0; i < qtContainerElements["length"].as<int>(); ++i) {
124 emscripten::val element = qtContainerElements[i].as<emscripten::val>();
125 if (element.isNull() || element.isUndefined())
126 qWarning() << "Skipping null or undefined element in qtContainerElements";
127 else
128 filtered.call<void>("push", element);
129 }
130 } else {
131 // No screens, which may or may not be intended
132 qWarning() << "The qtContainerElements module property was not set or is invalid. "
133 "Proceeding with no screens.";
134 }
136
137 // install browser window resize handler
138 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE,
139 [](int, const EmscriptenUiEvent *, void *) -> EM_BOOL {
140 // This resize event is called when the HTML window is
141 // resized. Depending on the page layout the elements might
142 // also have been resized, so we update the Qt screen sizes
143 // (and canvas render sizes).
144 if (QWasmIntegration *integration = QWasmIntegration::get())
145 integration->resizeAllScreens();
146 return EM_FALSE;
147 });
148
149 // install visualViewport resize handler which picks up size and scale change on mobile.
150 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
151 if (!visualViewport.isUndefined()) {
152 visualViewport.call<void>("addEventListener", val("resize"),
153 val::module_property("qtResizeAllScreens"));
154 }
155#if QT_CONFIG(draganddrop)
156 m_drag = std::make_unique<QWasmDrag>();
157#endif
158}
159
161{
162 // Remove event listener
163 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, nullptr);
164 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
165 if (!visualViewport.isUndefined()) {
166 visualViewport.call<void>("removeEventListener", val("resize"),
167 val::module_property("qtResizeAllScreens"));
168 }
169
170 delete m_fontDb;
171 delete m_desktopServices;
172#if QT_CONFIG(accessibility)
173 delete m_accessibility;
174#endif
175
176 for (const auto &elementAndScreen : m_screens)
177 elementAndScreen.wasmScreen->deleteScreen();
178
179 m_screens.clear();
180
181 s_instance = nullptr;
182}
183
184bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
185{
186 switch (cap) {
187 case ThreadedPixmaps: return true;
188 case OpenGL: return true;
189 case ThreadedOpenGL: return false;
190 case MultipleWindows: return true;
191 case WindowManagement: return true;
192 case ForeignWindows: return true;
193 case OpenGLOnRasterSurface: return true;
194 case OffscreenSurface: return true;
195 default: return QPlatformIntegration::hasCapability(cap);
196 }
197}
198
199QWasmWindow *QWasmIntegration::createWindow(QWindow *window, WId nativeHandle) const
200{
201 auto *wasmScreen = QWasmScreen::get(window->screen());
202 QWasmCompositor *compositor = wasmScreen->compositor();
203 return new QWasmWindow(window, compositor,
204 m_backingStores.value(window), nativeHandle);
205}
206
208{
209 return createWindow(window, 0);
210}
211
212QPlatformWindow *QWasmIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
213{
214 return createWindow(window, nativeHandle);
215}
216
218{
219 QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
220 QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window);
221 m_backingStores.insert(window, backingStore);
222 return backingStore;
223}
224
226{
227 m_backingStores.remove(window);
228}
229
231{
233 {
234 for (const auto &elementAndScreen : m_screens) {
235 elementAndScreen.wasmScreen->compositor()->requestUpdate();
236 }
237 }
238}
239
240#ifndef QT_NO_OPENGL
241QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
242{
243 return new QWasmOpenGLContext(context);
244}
245#endif
246
248{
249 auto icStrs = QPlatformInputContextFactory::requested();
250 if (!icStrs.isEmpty()) {
251 m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
252 m_wasmInputContext = nullptr;
253 } else {
254 m_inputContext.reset(new QWasmInputContext());
255 m_wasmInputContext = static_cast<QWasmInputContext *>(m_inputContext.data());
256 }
257}
258
260{
261 return m_inputContext.data();
262}
263
264QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
265{
266 return new QWasmOffscreenSurface(surface);
267}
268
270{
271 if (m_fontDb == nullptr)
272 m_fontDb = new QWasmFontDatabase;
273
274 return m_fontDb;
275}
276
278{
279 return new QWasmEventDispatcher(m_suspendResume);
280}
281
282QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
283{
284 switch (hint) {
285 case ShowIsFullScreen:
286 return true;
287 case UnderlineShortcut:
288 return platform() != Platform::MacOS;
289 default:
290 return QPlatformIntegration::styleHint(hint);
291 }
292}
293
294Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) const
295{
296 // Don't maximize dialogs or popups
297 if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup))
298 return Qt::WindowNoState;
299
300 return QPlatformIntegration::defaultWindowState(flags);
301}
302
304{
305 return QStringList() << "webassembly"_L1;
306}
307
309{
310 if (name == "webassembly"_L1)
311 return new QWasmTheme;
312 return QPlatformIntegration::createPlatformTheme(name);
313}
314
316{
317 if (m_desktopServices == nullptr)
318 m_desktopServices = new QWasmServices();
319 return m_desktopServices;
320}
321
322#if QT_CONFIG(clipboard)
323QPlatformClipboard* QWasmIntegration::clipboard() const
324{
325 return m_clipboard;
326}
327#endif
328
329#ifndef QT_NO_ACCESSIBILITY
331{
332 return m_accessibility;
333}
334#endif
335
337{
338 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
339 QList<ScreenMapping> newScreens;
340
341 QList<QWasmScreen *> screensToDelete;
342 std::transform(m_screens.begin(), m_screens.end(), std::back_inserter(screensToDelete),
343 [](const ScreenMapping &mapping) { return mapping.wasmScreen; });
344
345 for (int i = 0; i < elementArray["length"].as<int>(); ++i) {
346 const auto element = elementArray[i];
347 const auto it = std::find_if(
348 m_screens.begin(), m_screens.end(),
349 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
350 QWasmScreen *screen;
351 if (it != m_screens.end()) {
352 screen = it->wasmScreen;
353 screensToDelete.erase(std::remove_if(screensToDelete.begin(), screensToDelete.end(),
354 [screen](const QWasmScreen *removedScreen) {
355 return removedScreen == screen;
356 }),
357 screensToDelete.end());
358 } else {
359 screen = new QWasmScreen(element);
360 QWindowSystemInterface::handleScreenAdded(screen);
361 }
362 newScreens.push_back({element, screen});
363 }
364
365 std::for_each(screensToDelete.begin(), screensToDelete.end(),
366 [](QWasmScreen *removed) { removed->deleteScreen(); });
367
368 m_screens = newScreens;
369 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
370 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
371 QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter);
372}
373
375{
376 Q_ASSERT_X(m_screens.end()
377 == std::find_if(m_screens.begin(), m_screens.end(),
378 [&element](const ScreenMapping &screen) {
379 return screen.emscriptenVal == element;
380 }),
381 Q_FUNC_INFO, "Double-add of an element");
382
383 QWasmScreen *screen = new QWasmScreen(element);
384 QWindowSystemInterface::handleScreenAdded(screen);
385 m_screens.push_back({element, screen});
386}
387
389{
390 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
391
392 const auto it =
393 std::find_if(m_screens.begin(), m_screens.end(),
394 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
395 if (it == m_screens.end()) {
396 qWarning() << "Attempt to remove a nonexistent screen.";
397 return;
398 }
399
400 QWasmScreen *removedScreen = it->wasmScreen;
401 removedScreen->deleteScreen();
402
403 m_screens.erase(std::remove_if(m_screens.begin(), m_screens.end(),
404 [removedScreen](const ScreenMapping &mapping) {
405 return removedScreen == mapping.wasmScreen;
406 }),
407 m_screens.end());
408 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
409 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
410 QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter);
411}
412
413void QWasmIntegration::resizeScreen(const emscripten::val &element)
414{
415 auto it = std::find_if(m_screens.begin(), m_screens.end(),
416 [&] (const ScreenMapping &candidate) { return candidate.emscriptenVal.equals(element); });
417 if (it == m_screens.end()) {
418 qWarning() << "Attempting to resize non-existing screen for element"
419 << QString::fromEcmaString(element["id"]);
420 return;
421 }
422 it->wasmScreen->updateQScreenSize();
423}
424
426{
427 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
428 if (dpi.isUndefined())
429 return;
430 qreal dpiValue = dpi.as<qreal>();
431 for (const auto &elementAndScreen : m_screens)
432 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(elementAndScreen.wasmScreen->screen(), dpiValue, dpiValue);
433}
434
436{
437 for (const auto &elementAndScreen : m_screens)
438 elementAndScreen.wasmScreen->updateQScreenSize();
439}
440
442{
443 m_fontDb->populateLocalFontFamilies(families);
444}
445
446quint64 QWasmIntegration::getTimestamp()
447{
448 return emscripten_performance_now();
449}
450
451#if QT_CONFIG(draganddrop)
452QPlatformDrag *QWasmIntegration::drag() const
453{
454 return m_drag.get();
455}
456#endif // QT_CONFIG(draganddrop)
457
458QT_END_NAMESPACE
static bool releaseRequestUpdateHold()
QWasmCompositor(QWasmScreen *screen)
QPlatformInputContext * inputContext() const override
Returns the platforms input context.
void setContainerElements(emscripten::val elementArray)
static QWasmIntegration * get()
Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const override
QPlatformFontDatabase * fontDatabase() const override
Accessor for the platform integration's fontdatabase.
void addContainerElement(emscripten::val elementArray)
void initialize() override
Performs initialization steps that depend on having an event dispatcher available.
void removeContainerElement(emscripten::val elementArray)
QAbstractEventDispatcher * createEventDispatcher() const override
Factory function for the GUI event dispatcher.
QStringList themeNames() const override
void loadLocalFontFamilies(emscripten::val families)
QPlatformOpenGLContext * createPlatformOpenGLContext(QOpenGLContext *context) const override
Factory function for QPlatformOpenGLContext.
void removeBackingStore(QWindow *window)
QPlatformBackingStore * createPlatformBackingStore(QWindow *window) const override
Factory function for QPlatformBackingStore.
QPlatformServices * services() const override
QPlatformWindow * createForeignWindow(QWindow *window, WId nativeHandle) const override
QPlatformTheme * createPlatformTheme(const QString &name) const override
void resizeScreen(const emscripten::val &canvas)
QVariant styleHint(QPlatformIntegration::StyleHint hint) const override
bool hasCapability(QPlatformIntegration::Capability cap) const override
QPlatformWindow * createPlatformWindow(QWindow *window) const override
Factory function for QPlatformWindow.
QPlatformAccessibility * accessibility() const override
QWasmOffscreenSurface(QOffscreenSurface *offscreenSurface)
QWasmOpenGLContext(QOpenGLContext *context)
void deleteScreen()
friend class QWasmCompositor
QJSValue function
Definition qjsengine.cpp:9
Combined button and popup list for selecting options.
EMSCRIPTEN_BINDINGS(qtQWasmIntegraton)
static void addContainerElement(emscripten::val element)
static void removeContainerElement(emscripten::val element)
QT_BEGIN_NAMESPACE void qt_set_sequence_auto_mnemonic(bool)
static void loadLocalFontFamilies(emscripten::val event)
static void resizeAllScreens(emscripten::val event)
static void resizeContainerElement(emscripten::val element)
static void setContainerElements(emscripten::val elementArray)
static void qtUpdateDpi()
QT_BEGIN_NAMESPACE Platform platform()