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