Qt 6.x
The Qt SDK
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
4#include "qwasmintegration.h"
6#include "qwasmcompositor.h"
8#include "qwasmtheme.h"
9#include "qwasmclipboard.h"
10#include "qwasmaccessibility.h"
11#include "qwasmservices.h"
13#include "qwasmplatform.h"
14#include "qwasmwindow.h"
15#include "qwasmbackingstore.h"
16#include "qwasmfontdatabase.h"
17#if defined(Q_OS_UNIX)
18#include <QtGui/private/qgenericunixeventdispatcher_p.h>
19#endif
20#include <qpa/qplatformwindow.h>
21#include <QtGui/qscreen.h>
22#include <qpa/qwindowsysteminterface.h>
23#include <QtCore/qcoreapplication.h>
24#include <qpa/qplatforminputcontextfactory_p.h>
25
26#include <emscripten/bind.h>
27#include <emscripten/val.h>
28
29// this is where EGL headers are pulled in, make sure it is last
30#include "qwasmscreen.h"
31#include <private/qsimpledrag_p.h>
32
34
35extern void qt_set_sequence_auto_mnemonic(bool);
36
37using namespace emscripten;
38
39using namespace Qt::StringLiterals;
40
41static void setContainerElements(emscripten::val elementArray)
42{
44}
45
46static void addContainerElement(emscripten::val element)
47{
49}
50
51static void removeContainerElement(emscripten::val element)
52{
54}
55
56static void resizeContainerElement(emscripten::val element)
57{
59}
60
61static void qtUpdateDpi()
62{
64}
65
66static void resizeAllScreens(emscripten::val event)
67{
70}
71
72EMSCRIPTEN_BINDINGS(qtQWasmIntegraton)
73{
74 function("qtSetContainerElements", &setContainerElements);
75 function("qtAddContainerElement", &addContainerElement);
76 function("qtRemoveContainerElement", &removeContainerElement);
77 function("qtResizeContainerElement", &resizeContainerElement);
78 function("qtUpdateDpi", &qtUpdateDpi);
79 function("qtResizeAllScreens", &resizeAllScreens);
80}
81
82QWasmIntegration *QWasmIntegration::s_instance;
83
85 : m_fontDb(nullptr)
86 , m_desktopServices(nullptr)
87 , m_clipboard(new QWasmClipboard)
88#if QT_CONFIG(accessibility)
89 , m_accessibility(new QWasmAccessibility)
90#endif
91{
92 s_instance = this;
93
96
97 touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>();
98
99 // Create screens for container elements. Each container element will ultimately become a
100 // div element. Qt historically supported supplying canvas for screen elements - these elements
101 // will be transformed into divs and warnings about deprecation will be printed. See
102 // QWasmScreen ctor.
103 emscripten::val filtered = emscripten::val::array();
104 emscripten::val qtContainerElements = val::module_property("qtContainerElements");
105 if (qtContainerElements.isArray()) {
106 for (int i = 0; i < qtContainerElements["length"].as<int>(); ++i) {
107 emscripten::val element = qtContainerElements[i].as<emscripten::val>();
108 if (element.isNull() || element.isUndefined())
109 qWarning() << "Skipping null or undefined element in qtContainerElements";
110 else
111 filtered.call<void>("push", element);
112 }
113 } else {
114 // No screens, which may or may not be intended
115 qWarning() << "The qtContainerElements module property was not set or is invalid. "
116 "Proceeding with no screens.";
117 }
118 setContainerElements(filtered);
119
120 // install browser window resize handler
121 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE,
122 [](int, const EmscriptenUiEvent *, void *) -> int {
123 // This resize event is called when the HTML window is
124 // resized. Depending on the page layout the elements might
125 // also have been resized, so we update the Qt screen sizes
126 // (and canvas render sizes).
127 if (QWasmIntegration *integration = QWasmIntegration::get())
128 integration->resizeAllScreens();
129 return 0;
130 });
131
132 // install visualViewport resize handler which picks up size and scale change on mobile.
133 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
134 if (!visualViewport.isUndefined()) {
135 visualViewport.call<void>("addEventListener", val("resize"),
136 val::module_property("qtResizeAllScreens"));
137 }
138 m_drag = std::make_unique<QSimpleDrag>();
139}
140
142{
143 // Remove event listener
144 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, nullptr);
145 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
146 if (!visualViewport.isUndefined()) {
147 visualViewport.call<void>("removeEventListener", val("resize"),
148 val::module_property("qtResizeAllScreens"));
149 }
150
151 delete m_fontDb;
152 delete m_desktopServices;
153 if (m_platformInputContext)
154 delete m_platformInputContext;
155#if QT_CONFIG(accessibility)
156 delete m_accessibility;
157#endif
158
159 for (const auto &elementAndScreen : m_screens)
160 elementAndScreen.wasmScreen->deleteScreen();
161
162 m_screens.clear();
163
164 s_instance = nullptr;
165}
166
168{
169 switch (cap) {
170 case ThreadedPixmaps: return true;
171 case OpenGL: return true;
172 case ThreadedOpenGL: return false;
173 case RasterGLSurface: return false; // to enable this you need to fix qopenglwidget and quickwidget for wasm
174 case MultipleWindows: return true;
175 case WindowManagement: return true;
176 case OpenGLOnRasterSurface: return true;
178 }
179}
180
182{
183 auto *wasmScreen = QWasmScreen::get(window->screen());
184 QWasmCompositor *compositor = wasmScreen->compositor();
185 return new QWasmWindow(window, wasmScreen->deadKeySupport(), compositor,
186 m_backingStores.value(window));
187}
188
190{
193 m_backingStores.insert(window, backingStore);
194 return backingStore;
195}
196
198{
199 m_backingStores.remove(window);
200}
201
202#ifndef QT_NO_OPENGL
204{
205 return new QWasmOpenGLContext(context);
206}
207#endif
208
210{
211 if (qgetenv("QT_IM_MODULE").isEmpty() && touchPoints < 1)
212 return;
213
215 if (!icStr.isNull())
216 m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
217 else
218 m_inputContext.reset(new QWasmInputContext());
219}
220
222{
223 return m_inputContext.data();
224}
225
227{
228 return new QWasmOffscreenSurface(surface);
229}
230
232{
233 if (m_fontDb == nullptr)
234 m_fontDb = new QWasmFontDatabase;
235
236 return m_fontDb;
237}
238
240{
241 return new QWasmEventDispatcher;
242}
243
245{
246 switch (hint) {
247 case ShowIsFullScreen:
248 return true;
250 return platform() != Platform::MacOS;
251 default:
253 }
254}
255
257{
258 // Don't maximize dialogs or popups
259 if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup))
260 return Qt::WindowNoState;
261
263}
264
266{
267 return QStringList() << "webassembly"_L1;
268}
269
271{
272 if (name == "webassembly"_L1)
273 return new QWasmTheme;
275}
276
278{
279 if (m_desktopServices == nullptr)
280 m_desktopServices = new QWasmServices();
281 return m_desktopServices;
282}
283
285{
286 return m_clipboard;
287}
288
289#ifndef QT_NO_ACCESSIBILITY
290QPlatformAccessibility *QWasmIntegration::accessibility() const
291{
292 return m_accessibility;
293}
294#endif
295
296void QWasmIntegration::setContainerElements(emscripten::val elementArray)
297{
298 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
299 QList<ScreenMapping> newScreens;
300
301 QList<QWasmScreen *> screensToDelete;
302 std::transform(m_screens.begin(), m_screens.end(), std::back_inserter(screensToDelete),
303 [](const ScreenMapping &mapping) { return mapping.wasmScreen; });
304
305 for (int i = 0; i < elementArray["length"].as<int>(); ++i) {
306 const auto element = elementArray[i];
307 const auto it = std::find_if(
308 m_screens.begin(), m_screens.end(),
309 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
311 if (it != m_screens.end()) {
312 screen = it->wasmScreen;
313 screensToDelete.erase(std::remove_if(screensToDelete.begin(), screensToDelete.end(),
314 [screen](const QWasmScreen *removedScreen) {
315 return removedScreen == screen;
316 }),
317 screensToDelete.end());
318 } else {
319 screen = new QWasmScreen(element);
321 }
322 newScreens.push_back({element, screen});
323 }
324
325 std::for_each(screensToDelete.begin(), screensToDelete.end(),
326 [](QWasmScreen *removed) { removed->deleteScreen(); });
327
328 m_screens = newScreens;
329 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
330 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
332}
333
334void QWasmIntegration::addContainerElement(emscripten::val element)
335{
336 Q_ASSERT_X(m_screens.end()
337 == std::find_if(m_screens.begin(), m_screens.end(),
338 [&element](const ScreenMapping &screen) {
339 return screen.emscriptenVal == element;
340 }),
341 Q_FUNC_INFO, "Double-add of an element");
342
343 QWasmScreen *screen = new QWasmScreen(element);
345 m_screens.push_back({element, screen});
346}
347
348void QWasmIntegration::removeContainerElement(emscripten::val element)
349{
350 const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
351
352 const auto it =
353 std::find_if(m_screens.begin(), m_screens.end(),
354 [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; });
355 if (it == m_screens.end()) {
356 qWarning() << "Attempt to remove a nonexistent screen.";
357 return;
358 }
359
360 QWasmScreen *removedScreen = it->wasmScreen;
361 removedScreen->deleteScreen();
362
363 m_screens.erase(std::remove_if(m_screens.begin(), m_screens.end(),
364 [removedScreen](const ScreenMapping &mapping) {
365 return removedScreen == mapping.wasmScreen;
366 }),
367 m_screens.end());
368 auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen;
369 if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore)
371}
372
373void QWasmIntegration::resizeScreen(const emscripten::val &element)
374{
375 auto it = std::find_if(m_screens.begin(), m_screens.end(),
376 [&] (const ScreenMapping &candidate) { return candidate.emscriptenVal.equals(element); });
377 if (it == m_screens.end()) {
378 qWarning() << "Attempting to resize non-existing screen for element"
379 << QString::fromJsString(element["id"]);
380 return;
381 }
382 it->wasmScreen->updateQScreenAndCanvasRenderSize();
383}
384
386{
387 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
388 if (dpi.isUndefined())
389 return;
390 qreal dpiValue = dpi.as<qreal>();
391 for (const auto &elementAndScreen : m_screens)
392 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(elementAndScreen.wasmScreen->screen(), dpiValue, dpiValue);
393}
394
396{
397 for (const auto &elementAndScreen : m_screens)
398 elementAndScreen.wasmScreen->updateQScreenAndCanvasRenderSize();
399}
400
402{
403 return emscripten_performance_now();
404}
405
406#if QT_CONFIG(draganddrop)
407QPlatformDrag *QWasmIntegration::drag() const
408{
409 return m_drag.get();
410}
411#endif // QT_CONFIG(draganddrop)
412
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:956
T value(const Key &key) const noexcept
Definition qhash.h:1044
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:882
void push_back(parameter_type t)
Definition qlist.h:672
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
void clear()
Definition qlist.h:417
\inmodule QtGui
\inmodule QtGui
The QPlatformBackingStore class provides the drawing area for top-level windows.
The QPlatformClipboard class provides an abstraction for the system clipboard.
The QPlatformDrag class provides an abstraction for drag.
The QPlatformFontDatabase class makes it possible to customize how fonts are discovered and how they ...
static QPlatformInputContext * create()
The QPlatformInputContext class abstracts the input method dependent data and composing state.
virtual QVariant styleHint(StyleHint hint) const
virtual Qt::WindowState defaultWindowState(Qt::WindowFlags) const
virtual bool hasCapability(Capability cap) const
virtual QPlatformTheme * createPlatformTheme(const QString &name) const
Capability
Capabilities are used to determine specific features of a platform integration.
The QPlatformOpenGLContext class provides an abstraction for native GL contexts.
The QPlatformServices provides the backend for desktop-related functionality.
The QPlatformTheme class allows customizing the UI based on themes.
The QPlatformWindow class provides an abstraction for top-level windows.
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:898
\inmodule QtCore
Definition qvariant.h:64
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.
QPlatformOffscreenSurface * createPlatformOffscreenSurface(QOffscreenSurface *surface) const override
Factory function for QOffscreenSurface.
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
QPlatformOpenGLContext * createPlatformOpenGLContext(QOpenGLContext *context) const override
Factory function for QPlatformOpenGLContext.
void removeBackingStore(QWindow *window)
QPlatformBackingStore * createPlatformBackingStore(QWindow *window) const override
Factory function for QPlatformBackingStore.
static quint64 getTimestamp()
QPlatformServices * services() 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
static QWasmScreen * get(QPlatformScreen *screen)
void deleteScreen()
QWasmCompositor * compositor()
static void handlePrimaryScreenChanged(QPlatformScreen *newPrimary)
Should be called whenever the primary screen changes.
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary=false)
Should be called by the implementation whenever a new screen is added.
static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY)
\inmodule QtGui
Definition qwindow.h:63
QSet< QString >::iterator it
Combined button and popup list for selecting options.
WindowState
Definition qnamespace.h:250
@ WindowNoState
Definition qnamespace.h:251
@ Popup
Definition qnamespace.h:210
@ Dialog
Definition qnamespace.h:207
static void * context
#define Q_FUNC_INFO
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b)
#define qWarning
Definition qlogging.h:162
static QOpenGLCompositor * compositor
GLbitfield flags
GLuint name
struct _cl_event * event
GLuint GLfloat * val
GLenum GLenum GLenum GLenum mapping
GLenum cap
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
QScreen * screen
[1]
Definition main.cpp:29
#define QT_CONFIG(feature)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:56
double qreal
Definition qtypes.h:92
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 resizeAllScreens(emscripten::val event)
static void resizeContainerElement(emscripten::val element)
static void setContainerElements(emscripten::val elementArray)
static void qtUpdateDpi()
QT_BEGIN_NAMESPACE Platform platform()
if(qFloatDistance(a, b)<(1<< 7))
[0]
QObject::connect nullptr
aWidget window() -> setWindowTitle("New Window Title")
[2]