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