Qt 6.x
The Qt SDK
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_shadowContainer(emscripten::val::undefined()),
32 m_compositor(new QWasmCompositor(this)),
33 m_deadKeySupport(std::make_unique<QWasmDeadKeySupport>())
34{
35 auto document = m_container["ownerDocument"];
36 // Each screen is represented by a div container. All of the windows exist therein as
37 // its children. Qt versions < 6.5 used to represent screens as canvas. Support that by
38 // transforming the canvas into a div.
39 if (m_container["tagName"].call<std::string>("toLowerCase") == "canvas") {
40 qWarning() << "Support for canvas elements as an element backing screen is deprecated. The "
41 "canvas provided for the screen will be transformed into a div.";
42 auto container = document.call<emscripten::val>("createElement", emscripten::val("div"));
43 m_container["parentNode"].call<void>("replaceChild", container, m_container);
44 m_container = container;
45 }
46 auto shadowOptions = emscripten::val::object();
47 shadowOptions.set("mode", "open");
48 auto shadow = m_container.call<emscripten::val>("attachShadow", shadowOptions);
49
50 m_shadowContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
51
52 shadow.call<void>("appendChild", QWasmCSSStyle::createStyleElement(m_shadowContainer));
53
54 shadow.call<void>("appendChild", m_shadowContainer);
55
56 m_shadowContainer.set("id", std::string("qt-screen-") + std::to_string(uintptr_t(this)));
57
58 m_shadowContainer["classList"].call<void>("add", std::string("qt-screen"));
59
60 // Disable the default context menu; Qt applications typically
61 // provide custom right-click behavior.
62 m_onContextMenu = std::make_unique<qstdweb::EventCallback>(
63 m_shadowContainer, "contextmenu",
64 [](emscripten::val event) { event.call<void>("preventDefault"); });
65 // Create "specialHTMLTargets" mapping for the canvas - the element might be unreachable based
66 // on its id only under some conditions, like the target being embedded in a shadow DOM or a
67 // subframe.
68 emscripten::val::module_property("specialHTMLTargets")
69 .set(eventTargetId().toStdString(), m_shadowContainer);
70
71 emscripten::val::module_property("specialHTMLTargets")
72 .set(outerScreenId().toStdString(), m_container);
73
75 m_shadowContainer.call<void>("focus");
76
77 m_touchDevice = std::make_unique<QPointingDevice>(
82 10, 0);
83
85}
86
88{
89 emscripten::val::module_property("specialHTMLTargets")
90 .set(eventTargetId().toStdString(), emscripten::val::undefined());
91
92 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
93 emscripten::val(intptr_t(0)));
94}
95
97{
98 // Deletes |this|!
100}
101
103{
104 return static_cast<QWasmScreen *>(screen);
105}
106
108{
109 if (!screen)
110 return nullptr;
111 return get(screen->handle());
112}
113
115{
116 return m_compositor.get();
117}
118
119emscripten::val QWasmScreen::element() const
120{
121 return m_shadowContainer;
122}
123
125{
126 // Return a globally unique id for the canvas. We can choose any string,
127 // as long as it starts with a "!".
128 return QString("!qtcanvas_%1").arg(uintptr_t(this));
129}
130
132{
133 return QString("!outerscreen_%1").arg(uintptr_t(this));
134}
135
137{
138 return m_geometry;
139}
140
142{
143 return m_depth;
144}
145
147{
148 return m_format;
149}
150
152{
153 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
154 if (!dpi.isUndefined()) {
155 qreal dpiValue = dpi.as<qreal>();
156 return QDpi(dpiValue, dpiValue);
157 }
158 const qreal defaultDpi = 96;
159 return QDpi(defaultDpi, defaultDpi);
160}
161
163{
164 // window.devicePixelRatio gives us the scale factor between CSS and device pixels.
165 // This property reflects hardware configuration, and also browser zoom on desktop.
166 //
167 // window.visualViewport.scale gives us the zoom factor on mobile. If the html page is
168 // configured with "<meta name="viewport" content="width=device-width">" then this scale
169 // factor will be 1. Omitting the viewport configuration typically results on a zoomed-out
170 // viewport, with a scale factor <1. User pinch-zoom will change the scale factor; an event
171 // handler is installed in the QWasmIntegration constructor. Changing zoom level on desktop
172 // does not appear to change visualViewport.scale.
173 //
174 // The effective devicePixelRatio is the product of these two scale factors, upper-bounded
175 // by window.devicePixelRatio in order to avoid e.g. allocating a 10x widget backing store.
176 double dpr = emscripten::val::global("window")["devicePixelRatio"].as<double>();
177 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
178 double scale = visualViewport.isUndefined() ? 1.0 : visualViewport["scale"].as<double>();
179 double effectiveDevicePixelRatio = std::min(dpr * scale, dpr);
180 return qreal(effectiveDevicePixelRatio);
181}
182
184{
185 return QString::fromJsString(m_shadowContainer["id"]);
186}
187
189{
190 return const_cast<QWasmCursor *>(&m_cursor);
191}
192
194{
195 if (!screen())
196 return;
198}
199
201{
202 return activeChild() ? activeChild()->window() : nullptr;
203}
204
206{
207 const auto found =
208 std::find_if(childStack().begin(), childStack().end(), [&p](const QWasmWindow *window) {
209 const QRect geometry = window->windowFrameGeometry();
210
211 return window->isVisible() && geometry.contains(p);
212 });
213 return found != childStack().end() ? (*found)->window() : nullptr;
214}
215
217{
218 return geometry().topLeft() + p;
219}
220
222{
223 const auto geometryF = screen()->geometry().toRectF();
224 return QPointF(qBound(geometryF.left(), p.x(), geometryF.right()),
225 qBound(geometryF.top(), p.y(), geometryF.bottom()));
226}
227
229{
230 m_geometry = QRect();
231}
232
234{
235 m_geometry = rect;
239}
240
243{
245 if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
246 && childStack().size() == 1) {
247 child->window()->setFlag(Qt::WindowStaysOnBottomHint);
248 }
250 m_compositor->onWindowTreeChanged(changeType, child);
251}
252
254{
255 // The HTML canvas has two sizes: the CSS size and the canvas render size.
256 // The CSS size is determined according to standard CSS rules, while the
257 // render size is set using the "width" and "height" attributes. The render
258 // size must be set manually and is not auto-updated on CSS size change.
259 // Setting the render size to a value larger than the CSS size enables high-dpi
260 // rendering.
261 double css_width;
262 double css_height;
263 emscripten_get_element_css_size(outerScreenId().toUtf8().constData(), &css_width, &css_height);
264 QSizeF cssSize(css_width, css_height);
265
266 QSizeF canvasSize = cssSize * devicePixelRatio();
267
268 m_shadowContainer.set("width", canvasSize.width());
269 m_shadowContainer.set("height", canvasSize.height());
270
271 // Returns the html elements document/body position
272 auto getElementBodyPosition = [](const emscripten::val &element) -> QPoint {
273 emscripten::val bodyRect =
274 element["ownerDocument"]["body"].call<emscripten::val>("getBoundingClientRect");
275 emscripten::val canvasRect = element.call<emscripten::val>("getBoundingClientRect");
276 return QPoint(canvasRect["left"].as<int>() - bodyRect["left"].as<int>(),
277 canvasRect["top"].as<int>() - bodyRect["top"].as<int>());
278 };
279
280 setGeometry(QRect(getElementBodyPosition(m_shadowContainer), cssSize.toSize()));
281}
282
283void QWasmScreen::canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
284{
285 int count = entries["length"].as<int>();
286 if (count == 0)
287 return;
288 emscripten::val entry = entries[0];
289 QWasmScreen *screen = reinterpret_cast<QWasmScreen *>(
290 entry["target"][m_canvasResizeObserverCallbackContextPropertyName].as<intptr_t>());
291 if (!screen) {
292 qWarning() << "QWasmScreen::canvasResizeObserverCallback: missing screen pointer";
293 return;
294 }
295
296 // We could access contentBoxSize|contentRect|devicePixelContentBoxSize on the entry here, but
297 // these are not universally supported across all browsers. Get the sizes from the canvas
298 // instead.
299 screen->updateQScreenAndCanvasRenderSize();
300}
301
302EMSCRIPTEN_BINDINGS(qtCanvasResizeObserverCallback)
303{
304 emscripten::function("qtCanvasResizeObserverCallback",
306}
307
309{
310 emscripten::val ResizeObserver = emscripten::val::global("ResizeObserver");
311 if (ResizeObserver == emscripten::val::undefined())
312 return; // ResizeObserver API is not available
313 emscripten::val resizeObserver =
314 ResizeObserver.new_(emscripten::val::module_property("qtCanvasResizeObserverCallback"));
315 if (resizeObserver == emscripten::val::undefined())
316 return; // Something went horribly wrong
317
318 // We need to get back to this instance from the (static) resize callback;
319 // set a "data-" property on the canvas element.
320 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
321 emscripten::val(intptr_t(this)));
322
323 resizeObserver.call<void>("observe", m_shadowContainer);
324}
325
327{
328 return m_shadowContainer;
329}
330
332{
333 return nullptr;
334}
335
337{
339 for (auto *child : childStack()) {
340 QWindowList list = child->window()->findChildren<QWindow *>(Qt::FindChildrenRecursively);
341 std::transform(
342 list.begin(), list.end(), std::back_inserter(windows),
343 [](const QWindow *window) { return static_cast<QWasmWindow *>(window->handle()); });
345 }
346 return windows;
347}
348
Format
The following image formats are available in Qt.
Definition qimage.h:41
Definition qlist.h:74
void push_back(parameter_type t)
Definition qlist.h:672
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
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:214
\inmodule QtCore\reentrant
Definition qpoint.h:23
\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:220
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:851
constexpr QRectF toRectF() const noexcept
Definition qrect.h:843
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:207
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:390
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:321
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:324
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
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:94
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:239
INT_PTR intptr_t
QPair< qreal, qreal > QDpi
#define qWarning
Definition qlogging.h:162
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLenum GLsizei count
struct _cl_event * event
GLuint GLfloat * val
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:92
EMSCRIPTEN_BINDINGS(qtCanvasResizeObserverCallback)
QWasmWindowTreeNodeChangeType
QList< int > list
[14]
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent