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