Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmscreen.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 "qwasmscreen.h"
5
6#include "qwasmcompositor.h"
7#include "qwasmcssstyle.h"
8#include "qwasmintegration.h"
10#include "qwasmwindow.h"
11
12#include <emscripten/bind.h>
13#include <emscripten/val.h>
14
15#include <qpa/qwindowsysteminterface.h>
16#include <QtCore/qcoreapplication.h>
17#include <QtGui/qguiapplication.h>
18#include <private/qhighdpiscaling_p.h>
19
20#include <tuple>
21
23
24using namespace emscripten;
25
26const char *QWasmScreen::m_canvasResizeObserverCallbackContextPropertyName =
27 "data-qtCanvasResizeObserverCallbackContext";
28
29QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
30 : m_container(containerOrCanvas),
31 m_intermediateContainer(emscripten::val::undefined()),
32 m_shadowContainer(emscripten::val::undefined()),
33 m_compositor(new QWasmCompositor(this)),
34 m_deadKeySupport(std::make_unique<QWasmDeadKeySupport>())
35{
36 auto document = m_container["ownerDocument"];
37 // Each screen is represented by a div container. All of the windows exist therein as
38 // its children. Qt versions < 6.5 used to represent screens as canvas. Support that by
39 // transforming the canvas into a div.
40 if (m_container["tagName"].call<std::string>("toLowerCase") == "canvas") {
41 qWarning() << "Support for canvas elements as an element backing screen is deprecated. The "
42 "canvas provided for the screen will be transformed into a div.";
43 auto container = document.call<emscripten::val>("createElement", emscripten::val("div"));
44 m_container["parentNode"].call<void>("replaceChild", container, m_container);
45 m_container = container;
46 }
47
48 // Create an intermediate container which we can remove during cleanup in ~QWasmScreen().
49 // This is required due to the attachShadow() call below; there is no corresponding
50 // "detachShadow()" API to return the container to its previous state.
51 m_intermediateContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
52 m_intermediateContainer.set("id", std::string("qt-shadow-container"));
53 emscripten::val intermediateContainerStyle = m_intermediateContainer["style"];
54 intermediateContainerStyle.set("width", std::string("100%"));
55 intermediateContainerStyle.set("height", std::string("100%"));
56 m_container.call<void>("appendChild", m_intermediateContainer);
57
58 auto shadowOptions = emscripten::val::object();
59 shadowOptions.set("mode", "open");
60 auto shadow = m_intermediateContainer.call<emscripten::val>("attachShadow", shadowOptions);
61
62 m_shadowContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
63
64 shadow.call<void>("appendChild", QWasmCSSStyle::createStyleElement(m_shadowContainer));
65
66 shadow.call<void>("appendChild", m_shadowContainer);
67
68 m_shadowContainer.set("id", std::string("qt-screen-") + std::to_string(uintptr_t(this)));
69
70 m_shadowContainer["classList"].call<void>("add", std::string("qt-screen"));
71
72 // Disable the default context menu; Qt applications typically
73 // provide custom right-click behavior.
74 m_onContextMenu = std::make_unique<qstdweb::EventCallback>(
75 m_shadowContainer, "contextmenu",
76 [](emscripten::val event) { event.call<void>("preventDefault"); });
77 // Create "specialHTMLTargets" mapping for the canvas - the element might be unreachable based
78 // on its id only under some conditions, like the target being embedded in a shadow DOM or a
79 // subframe.
80 emscripten::val::module_property("specialHTMLTargets")
81 .set(eventTargetId().toStdString(), m_shadowContainer);
82
83 emscripten::val::module_property("specialHTMLTargets")
84 .set(outerScreenId().toStdString(), m_container);
85
87 m_shadowContainer.call<void>("focus");
88
89 m_touchDevice = std::make_unique<QPointingDevice>(
94 10, 0);
95 m_tabletDevice = std::make_unique<QPointingDevice>(
104 0, 0);
105
107}
108
110{
111 m_intermediateContainer.call<void>("remove");
112
113 emscripten::val::module_property("specialHTMLTargets")
114 .set(eventTargetId().toStdString(), emscripten::val::undefined());
115
116 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
117 emscripten::val(intptr_t(0)));
118}
119
121{
122 // Deletes |this|!
124}
125
127{
128 return static_cast<QWasmScreen *>(screen);
129}
130
132{
133 if (!screen)
134 return nullptr;
135 return get(screen->handle());
136}
137
139{
140 return m_compositor.get();
141}
142
143emscripten::val QWasmScreen::element() const
144{
145 return m_shadowContainer;
146}
147
149{
150 // Return a globally unique id for the canvas. We can choose any string,
151 // as long as it starts with a "!".
152 return QString("!qtcanvas_%1").arg(uintptr_t(this));
153}
154
156{
157 return QString("!outerscreen_%1").arg(uintptr_t(this));
158}
159
161{
162 return m_geometry;
163}
164
166{
167 return m_depth;
168}
169
171{
172 return m_format;
173}
174
176{
177 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
178 if (!dpi.isUndefined()) {
179 qreal dpiValue = dpi.as<qreal>();
180 return QDpi(dpiValue, dpiValue);
181 }
182 const qreal defaultDpi = 96;
183 return QDpi(defaultDpi, defaultDpi);
184}
185
187{
188 // window.devicePixelRatio gives us the scale factor between CSS and device pixels.
189 // This property reflects hardware configuration, and also browser zoom on desktop.
190 //
191 // window.visualViewport.scale gives us the zoom factor on mobile. If the html page is
192 // configured with "<meta name="viewport" content="width=device-width">" then this scale
193 // factor will be 1. Omitting the viewport configuration typically results on a zoomed-out
194 // viewport, with a scale factor <1. User pinch-zoom will change the scale factor; an event
195 // handler is installed in the QWasmIntegration constructor. Changing zoom level on desktop
196 // does not appear to change visualViewport.scale.
197 //
198 // The effective devicePixelRatio is the product of these two scale factors, upper-bounded
199 // by window.devicePixelRatio in order to avoid e.g. allocating a 10x widget backing store.
200 double dpr = emscripten::val::global("window")["devicePixelRatio"].as<double>();
201 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
202 double scale = visualViewport.isUndefined() ? 1.0 : visualViewport["scale"].as<double>();
203 double effectiveDevicePixelRatio = std::min(dpr * scale, dpr);
204 return qreal(effectiveDevicePixelRatio);
205}
206
208{
209 return QString::fromEcmaString(m_shadowContainer["id"]);
210}
211
213{
214 return const_cast<QWasmCursor *>(&m_cursor);
215}
216
223
225{
226 return activeChild() ? activeChild()->window() : nullptr;
227}
228
230{
231 const auto found =
232 std::find_if(childStack().begin(), childStack().end(), [&p](const QWasmWindow *window) {
233 const QRect geometry = window->windowFrameGeometry();
234
235 return window->isVisible() && geometry.contains(p);
236 });
237 return found != childStack().end() ? (*found)->window() : nullptr;
238}
239
241{
242 return geometry().topLeft() + p;
243}
244
246{
247 const auto geometryF = screen()->geometry().toRectF();
248 return QPointF(qBound(geometryF.left(), p.x(), geometryF.right()),
249 qBound(geometryF.top(), p.y(), geometryF.bottom()));
250}
251
253{
254 m_geometry = QRect();
255}
256
264
267{
269 if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
270 && childStack().size() == 1) {
271 child->window()->setFlag(Qt::WindowStaysOnBottomHint);
272 }
274 m_compositor->onWindowTreeChanged(changeType, child);
275}
276
278{
279 // The HTML canvas has two sizes: the CSS size and the canvas render size.
280 // The CSS size is determined according to standard CSS rules, while the
281 // render size is set using the "width" and "height" attributes. The render
282 // size must be set manually and is not auto-updated on CSS size change.
283 // Setting the render size to a value larger than the CSS size enables high-dpi
284 // rendering.
285 double css_width;
286 double css_height;
287 emscripten_get_element_css_size(outerScreenId().toUtf8().constData(), &css_width, &css_height);
288 QSizeF cssSize(css_width, css_height);
289
290 QSizeF canvasSize = cssSize * devicePixelRatio();
291
292 m_shadowContainer.set("width", canvasSize.width());
293 m_shadowContainer.set("height", canvasSize.height());
294
295 // Returns the html elements document/body position
296 auto getElementBodyPosition = [](const emscripten::val &element) -> QPoint {
297 emscripten::val bodyRect =
298 element["ownerDocument"]["body"].call<emscripten::val>("getBoundingClientRect");
299 emscripten::val canvasRect = element.call<emscripten::val>("getBoundingClientRect");
300 return QPoint(canvasRect["left"].as<int>() - bodyRect["left"].as<int>(),
301 canvasRect["top"].as<int>() - bodyRect["top"].as<int>());
302 };
303
304 setGeometry(QRect(getElementBodyPosition(m_shadowContainer), cssSize.toSize()));
305}
306
307void QWasmScreen::canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
308{
309 int count = entries["length"].as<int>();
310 if (count == 0)
311 return;
312 emscripten::val entry = entries[0];
313 QWasmScreen *screen = reinterpret_cast<QWasmScreen *>(
314 entry["target"][m_canvasResizeObserverCallbackContextPropertyName].as<intptr_t>());
315 if (!screen) {
316 qWarning() << "QWasmScreen::canvasResizeObserverCallback: missing screen pointer";
317 return;
318 }
319
320 // We could access contentBoxSize|contentRect|devicePixelContentBoxSize on the entry here, but
321 // these are not universally supported across all browsers. Get the sizes from the canvas
322 // instead.
323 screen->updateQScreenAndCanvasRenderSize();
324}
325
326EMSCRIPTEN_BINDINGS(qtCanvasResizeObserverCallback)
327{
328 emscripten::function("qtCanvasResizeObserverCallback",
330}
331
333{
334 emscripten::val ResizeObserver = emscripten::val::global("ResizeObserver");
335 if (ResizeObserver == emscripten::val::undefined())
336 return; // ResizeObserver API is not available
337 emscripten::val resizeObserver =
338 ResizeObserver.new_(emscripten::val::module_property("qtCanvasResizeObserverCallback"));
339 if (resizeObserver == emscripten::val::undefined())
340 return; // Something went horribly wrong
341
342 // We need to get back to this instance from the (static) resize callback;
343 // set a "data-" property on the canvas element.
344 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
345 emscripten::val(intptr_t(this)));
346
347 resizeObserver.call<void>("observe", m_shadowContainer);
348}
349
351{
352 return m_shadowContainer;
353}
354
356{
357 return nullptr;
358}
359
360QList<QWasmWindow *> QWasmScreen::allWindows()
361{
362 QList<QWasmWindow *> windows;
363 for (auto *child : childStack()) {
364 const QWindowList list = child->window()->findChildren<QWindow *>(Qt::FindChildrenRecursively);
365 for (auto child : list) {
366 auto handle = child->handle();
367 if (handle) {
368 auto wnd = static_cast<QWasmWindow *>(handle);
369 windows.push_back(wnd);
370 }
371 }
373 }
374 return windows;
375}
376
Format
The following image formats are available in Qt.
Definition qimage.h:41
void push_back(parameter_type t)
Definition qlist.h:675
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
The QPlatformCursor class provides information about pointer device events (movement,...
The QPlatformScreen class provides an abstraction for visual displays.
QScreen * screen() const
void resizeMaximizedWindows()
Convenience method to resize all the maximized and fullscreen windows of this platform screen.
QWindowList windows() const
Return all windows residing on this screen.
virtual QRect availableGeometry() const
Reimplement in subclass to return the pixel geometry of the available space This normally is the desk...
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qrect.cpp:855
constexpr QRectF toRectF() const noexcept
Definition qrect.h:857
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
QRect geometry
the screen's geometry in pixels
Definition qscreen.h:45
QPlatformScreen * handle() const
Get the platform screen handle.
Definition qscreen.cpp:83
\inmodule QtCore
Definition qsize.h:208
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:332
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:335
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QWasmScreen * get(QPlatformScreen *screen)
QString name() const override
void installCanvasResizeObserver()
emscripten::val element() const
QPointF mapFromLocal(const QPointF &p) const
void deleteScreen()
QWindow * topLevelAt(const QPoint &p) const override
Return the given top level window for a given position.
void setGeometry(const QRect &rect)
QPointF clipPoint(const QPointF &p) const
QWasmCompositor * compositor()
void updateQScreenAndCanvasRenderSize()
QPlatformCursor * cursor() const override
Reimplement this function in subclass to return the cursor of the screen.
QImage::Format format() const override
Reimplement in subclass to return the image format which corresponds to the screen format.
QWasmWindowTreeNode * parentNode() final
qreal devicePixelRatio() const override
Reimplement this function in subclass to return the device pixel ratio for the screen.
QWasmScreen(const emscripten::val &containerOrCanvas)
void resizeMaximizedWindows()
void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, QWasmWindow *child) final
QString eventTargetId() const
void invalidateSize()
QString outerScreenId() const
int depth() const override
Reimplement in subclass to return current depth of the screen.
QWindow * topWindow() const
QDpi logicalDpi() const override
Reimplement this function in subclass to return the logical horizontal and vertical dots per inch met...
static void canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
emscripten::val containerElement() final
QList< QWasmWindow * > allWindows()
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
QWasmWindow * activeChild() const
virtual void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, QWasmWindow *child)
const QWasmWindowStack & childStack() const
QWindow * window() const
Definition qwasmwindow.h:90
static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry, const QRect &newAvailableGeometry)
static void registerInputDevice(const QInputDevice *device)
static void handleScreenRemoved(QPlatformScreen *screen)
Should be called by the implementation whenever a screen is removed.
\inmodule QtGui
Definition qwindow.h:63
#define this
Definition dialogs.cpp:9
rect
[4]
Combined button and popup list for selecting options.
emscripten::val createStyleElement(emscripten::val parent)
@ FindChildrenRecursively
@ WindowStaysOnBottomHint
Definition qnamespace.h:240
INT_PTR intptr_t
QPair< qreal, qreal > QDpi
#define qWarning
Definition qlogging.h:166
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLenum GLsizei count
struct _cl_event * event
GLuint entry
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
static QT_BEGIN_NAMESPACE qreal dpr(const QWindow *w)
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QScreen * screen
[1]
Definition main.cpp:29
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
EMSCRIPTEN_BINDINGS(qtCanvasResizeObserverCallback)
QWasmWindowTreeNodeChangeType
QList< int > list
[14]
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]