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// Qt-Security score:significant reason:default
4
9#include "qwasmtheme.h"
10#if QT_CONFIG(clipboard)
11#include "qwasmclipboard.h"
12#endif
14#include "qwasmservices.h"
16#include "qwasmplatform.h"
17#include "qwasmwindow.h"
20#if QT_CONFIG(draganddrop)
21#include "qwasmdrag.h"
22#endif
23
24#include <qpa/qplatformwindow.h>
25#include <QtGui/qscreen.h>
26#include <qpa/qwindowsysteminterface.h>
27#include <QtCore/qcoreapplication.h>
28#include <qpa/qplatforminputcontextfactory_p.h>
29#include <qpa/qwindowsysteminterface_p.h>
30#include "private/qwasmsuspendresumecontrol_p.h"
31
32#include <emscripten/bind.h>
33#include <emscripten/val.h>
34
35// this is where EGL headers are pulled in, make sure it is last
36#include "qwasmscreen.h"
37#if QT_CONFIG(draganddrop)
38#include <private/qsimpledrag_p.h>
39#endif
40
42
43using namespace emscripten;
44
45using namespace Qt::StringLiterals;
46
47static void setContainerElements(emscripten::val elementArray)
48{
50}
51
56
61
66
71
72static void resizeAllScreens(emscripten::val event)
73{
74 Q_UNUSED(event);
76}
77
82
84{
85 function("qtSetContainerElements", &setContainerElements);
86 function("qtAddContainerElement", &addContainerElement);
87 function("qtRemoveContainerElement", &removeContainerElement);
88 function("qtResizeContainerElement", &resizeContainerElement);
89 function("qtUpdateDpi", &qtUpdateDpi);
90 function("qtResizeAllScreens", &resizeAllScreens);
91 function("qtLoadLocalFontFamilies", &loadLocalFontFamilies);
92}
93
95
96QWasmIntegration::QWasmIntegration()
97 : m_fontDb(nullptr)
98 , m_desktopServices(nullptr)
99#if QT_CONFIG(clipboard)
100 , m_clipboard(new QWasmClipboard)
101#endif
102#if QT_CONFIG(accessibility)
103 , m_accessibility(new QWasmAccessibility)
104#endif
105{
106 s_instance = this;
107
108 touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>();
109 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
110
111 // Create screens for container elements. Each container element will ultimately become a
112 // div element. Qt historically supported supplying canvas for screen elements - these elements
113 // will be transformed into divs and warnings about deprecation will be printed. See
114 // QWasmScreen ctor.
115 emscripten::val filtered = emscripten::val::array();
116 emscripten::val qtContainerElements = val::module_property("qtContainerElements");
117 if (qtContainerElements.isArray()) {
118 for (int i = 0; i < qtContainerElements["length"].as<int>(); ++i) {
119 emscripten::val element = qtContainerElements[i].as<emscripten::val>();
120 if (element.isNull() || element.isUndefined())
121 qWarning() << "Skipping null or undefined element in qtContainerElements";
122 else
123 filtered.call<void>("push", element);
124 }
125 } else {
126 // No screens, which may or may not be intended
127 qWarning() << "The qtContainerElements module property was not set or is invalid. "
128 "Proceeding with no screens.";
129 }
131
132 // install browser window resize handler
133 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE,
134 [](int, const EmscriptenUiEvent *, void *) -> EM_BOOL {
135 // This resize event is called when the HTML window is
136 // resized. Depending on the page layout the elements might
137 // also have been resized, so we update the Qt screen sizes
138 // (and canvas render sizes).
139 if (QWasmIntegration *integration = QWasmIntegration::get())
140 integration->resizeAllScreens();
141 return EM_FALSE;
142 });
143
144 // install visualViewport resize handler which picks up size and scale change on mobile.
145 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
146 if (!visualViewport.isUndefined()) {
147 visualViewport.call<void>("addEventListener", val("resize"),
148 val::module_property("qtResizeAllScreens"));
149 }
150#if QT_CONFIG(draganddrop)
151 m_drag = std::make_unique<QWasmDrag>();
152#endif
153}
154
156{
157 // Remove event listener
158 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, nullptr);
159 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
160 if (!visualViewport.isUndefined()) {
161 visualViewport.call<void>("removeEventListener", val("resize"),
162 val::module_property("qtResizeAllScreens"));
163 }
164
165 delete m_fontDb;
166 delete m_desktopServices;
167#if QT_CONFIG(accessibility)
168 delete m_accessibility;
169#endif
170
171 for (const auto &elementAndScreen : m_screens)
172 elementAndScreen.wasmScreen->deleteScreen();
173
174 m_screens.clear();
175 delete m_clipboard;
176 s_instance = nullptr;
177}
178
179bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
180{
181 switch (cap) {
182 case ThreadedPixmaps: return true;
183 case OpenGL: return true;
184 case ThreadedOpenGL: return false;
185 case MultipleWindows: return true;
186 case WindowManagement: return true;
187 case ForeignWindows: return true;
188 case OpenGLOnRasterSurface: return true;
189 case OffscreenSurface: return true;
190 default: return QPlatformIntegration::hasCapability(cap);
191 }
192}
193
194QWasmWindow *QWasmIntegration::createWindow(QWindow *window, WId nativeHandle) const
195{
196 auto *wasmScreen = QWasmScreen::get(window->screen());
197 QWasmCompositor *compositor = wasmScreen->compositor();
198 return new QWasmWindow(window, compositor,
199 m_backingStores.value(window), nativeHandle);
200}
201
203{
204 return createWindow(window, 0);
205}
206
207QPlatformWindow *QWasmIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
208{
209 return createWindow(window, nativeHandle);
210}
211
213{
214 QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
215 QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window);
216 m_backingStores.insert(window, backingStore);
217 return backingStore;
218}
219
221{
222 m_backingStores.remove(window);
223}
224
225#ifndef QT_NO_OPENGL
226QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
227{
228 return new QWasmOpenGLContext(context);
229}
230#endif
231
233{
234 auto icStrs = QPlatformInputContextFactory::requested();
235 if (!icStrs.isEmpty()) {
236 m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
237 m_wasmInputContext = nullptr;
238 } else {
239 m_inputContext.reset(new QWasmInputContext());
240 m_wasmInputContext = static_cast<QWasmInputContext *>(m_inputContext.data());
241 }
242}
243
245{
246 return m_inputContext.data();
247}
248
249QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
250{
251 return new QWasmOffscreenSurface(surface);
252}
253
255{
256 if (m_fontDb == nullptr)
257 m_fontDb = new QWasmFontDatabase;
258
259 return m_fontDb;
260}
261
266
267QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
268{
269 switch (hint) {
270 case ShowIsFullScreen:
271 return true;
272 case UnderlineShortcut:
273 return platform() != Platform::MacOS;
274 default:
275 return QPlatformIntegration::styleHint(hint);
276 }
277}
278
279Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) const
280{
281 // Don't maximize dialogs or popups
282 if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup))
283 return Qt::WindowNoState;
284
285 return QPlatformIntegration::defaultWindowState(flags);
286}
287
289{
290 return QStringList() << "webassembly"_L1;
291}
292
294{
295 if (name == "webassembly"_L1)
296 return new QWasmTheme;
297 return QPlatformIntegration::createPlatformTheme(name);
298}
299
301{
302 if (m_desktopServices == nullptr)
303 m_desktopServices = new QWasmServices();
304 return m_desktopServices;
305}
306
307#if QT_CONFIG(clipboard)
308QPlatformClipboard* QWasmIntegration::clipboard() const
309{
310 return m_clipboard;
311}
312#endif
313
314#ifndef QT_NO_ACCESSIBILITY
316{
317 return m_accessibility;
318}
319#endif
320
322{
323 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
324 QList<ScreenMapping> newScreens;
325
326 QList<QWasmScreen *> screensToDelete;
327 std::transform(m_screens.begin(), m_screens.end(), std::back_inserter(screensToDelete),
328 [](const ScreenMapping &mapping) { return mapping.wasmScreen; });
329
330 for (int i = 0; i < elementArray["length"].as<int>(); ++i) {
331 const auto element = elementArray[i];
332 const auto it = std::find_if(
333 m_screens.begin(), m_screens.end(),
334 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
335 QWasmScreen *screen;
336 if (it != m_screens.end()) {
337 screen = it->wasmScreen;
338 screensToDelete.erase(std::remove_if(screensToDelete.begin(), screensToDelete.end(),
339 [screen](const QWasmScreen *removedScreen) {
340 return removedScreen == screen;
341 }),
342 screensToDelete.end());
343 } else {
344 screen = new QWasmScreen(element);
345 QWindowSystemInterface::handleScreenAdded(screen);
346 }
347 newScreens.push_back({element, screen});
348 }
349
350 std::for_each(screensToDelete.begin(), screensToDelete.end(),
351 [](QWasmScreen *removed) { removed->deleteScreen(); });
352
353 m_screens = newScreens;
354 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
355 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
356 QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter);
357}
358
360{
361 Q_ASSERT_X(m_screens.end()
362 == std::find_if(m_screens.begin(), m_screens.end(),
363 [&element](const ScreenMapping &screen) {
364 return screen.emscriptenVal == element;
365 }),
366 Q_FUNC_INFO, "Double-add of an element");
367
368 QWasmScreen *screen = new QWasmScreen(element);
369 QWindowSystemInterface::handleScreenAdded(screen);
370 m_screens.push_back({element, screen});
371}
372
374{
375 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
376
377 const auto it =
378 std::find_if(m_screens.begin(), m_screens.end(),
379 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
380 if (it == m_screens.end()) {
381 qWarning() << "Attempt to remove a nonexistent screen.";
382 return;
383 }
384
385 QWasmScreen *removedScreen = it->wasmScreen;
386 removedScreen->deleteScreen();
387
388 m_screens.erase(std::remove_if(m_screens.begin(), m_screens.end(),
389 [removedScreen](const ScreenMapping &mapping) {
390 return removedScreen == mapping.wasmScreen;
391 }),
392 m_screens.end());
393 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
394 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
395 QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter);
396}
397
398void QWasmIntegration::resizeScreen(const emscripten::val &element)
399{
400 auto it = std::find_if(m_screens.begin(), m_screens.end(),
401 [&] (const ScreenMapping &candidate) { return candidate.emscriptenVal.equals(element); });
402 if (it == m_screens.end()) {
403 qWarning() << "Attempting to resize non-existing screen for element"
404 << QString::fromEcmaString(element["id"]);
405 return;
406 }
407 it->wasmScreen->updateQScreenSize();
408}
409
411{
412 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
413 if (dpi.isUndefined())
414 return;
415 qreal dpiValue = dpi.as<qreal>();
416 for (const auto &elementAndScreen : m_screens)
417 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(elementAndScreen.wasmScreen->screen(), dpiValue, dpiValue);
418}
419
421{
422 for (const auto &elementAndScreen : m_screens)
423 elementAndScreen.wasmScreen->updateQScreenSize();
424}
425
427{
428 m_fontDb->populateLocalFontFamilies(families);
429}
430
431quint64 QWasmIntegration::getTimestamp()
432{
433 return emscripten_performance_now();
434}
435
436#if QT_CONFIG(draganddrop)
437QPlatformDrag *QWasmIntegration::drag() const
438{
439 return m_drag.get();
440}
441#endif // QT_CONFIG(draganddrop)
442
443QT_END_NAMESPACE
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)
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()