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
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// Qt-Security score:significant reason:default
4
5#include "qwasmscreen.h"
6
11#include "qwasmwindow.h"
12
13#include <emscripten/val.h>
14
15#include <private/qwasmsuspendresumecontrol_p.h>
16#include <qpa/qwindowsysteminterface.h>
17#include <QtCore/qcoreapplication.h>
18#include <QtGui/qguiapplication.h>
19
20#include <tuple>
21
23
24using namespace emscripten;
25
26QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
27 : m_container(containerOrCanvas),
28 m_intermediateContainer(emscripten::val::undefined()),
29 m_shadowContainer(emscripten::val::undefined()),
30 m_compositor(new QWasmCompositor(this))
31{
32 auto document = m_container["ownerDocument"];
33
34 // Create an intermediate container which we can remove during cleanup in ~QWasmScreen().
35 // This is required due to the attachShadow() call below; there is no corresponding
36 // "detachShadow()" API to return the container to its previous state.
37 m_intermediateContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
38 m_intermediateContainer.set("id", std::string("qt-shadow-container"));
39 emscripten::val intermediateContainerStyle = m_intermediateContainer["style"];
40 intermediateContainerStyle.set("width", std::string("100%"));
41 intermediateContainerStyle.set("height", std::string("100%"));
42 m_container.call<void>("appendChild", m_intermediateContainer);
43 // Each screen is represented by a div container. All of the windows exist therein as
44 // its children. Qt versions < 6.5 used to represent screens as canvas elements; this
45 // is no longer supported.
46 if (m_container["tagName"].call<std::string>("toLowerCase") == "canvas")
47 qFatal() << "Qt does not support using a canvas element as the container element. Use a div element instead";
48
49 auto shadowOptions = emscripten::val::object();
50 shadowOptions.set("mode", "open");
51 auto shadow = m_intermediateContainer.call<emscripten::val>("attachShadow", shadowOptions);
52
53 m_shadowContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
54
55 shadow.call<void>("appendChild", QWasmCSSStyle::createStyleElement(m_shadowContainer));
56
57 shadow.call<void>("appendChild", m_shadowContainer);
58
59 m_shadowContainer.set("id", std::string("qt-screen-") + std::to_string(uintptr_t(this)));
60
61 m_shadowContainer["classList"].call<void>("add", std::string("qt-screen"));
62
63 // Disable the default context menu; Qt applications typically
64 // provide custom right-click behavior.
65 m_onContextMenu = std::make_unique<qstdweb::EventCallback>(
66 m_shadowContainer, "contextmenu",
67 [](emscripten::val event) { event.call<void>("preventDefault"); });
68 // Create "specialHTMLTargets" mapping for the canvas - the element might be unreachable based
69 // on its id only under some conditions, like the target being embedded in a shadow DOM or a
70 // subframe.
71 emscripten::val::module_property("specialHTMLTargets")
72 .set(outerScreenId().toStdString(), m_container);
73
76 m_shadowContainer.call<void>("focus");
77
78 m_touchDevice = std::make_unique<QPointingDevice>(
79 "touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
80 QPointingDevice::PointerType::Finger,
81 QPointingDevice::Capability::Position | QPointingDevice::Capability::Area
82 | QPointingDevice::Capability::NormalizedPosition,
83 10, 0);
84 m_tabletDevice = std::make_unique<QPointingDevice>(
85 "stylus", 2, QInputDevice::DeviceType::Stylus,
86 QPointingDevice::PointerType::Pen,
87 QPointingDevice::Capability::Position | QPointingDevice::Capability::Pressure
88 | QPointingDevice::Capability::NormalizedPosition
89 | QInputDevice::Capability::MouseEmulation
90 | QInputDevice::Capability::Hover | QInputDevice::Capability::Rotation
91 | QInputDevice::Capability::XTilt | QInputDevice::Capability::YTilt
92 | QInputDevice::Capability::TangentialPressure,
93 0, 0);
94
95 QWindowSystemInterface::registerInputDevice(m_touchDevice.get());
96}
97
99{
100 m_resizeObserver.call<void>("disconnect");
101 QWasmSuspendResumeControl::get()->removeEventHandler(m_resizeObserverHandlerIndex);
102 m_intermediateContainer.call<void>("remove");
103}
104
106{
107 // Deletes |this|!
108 QWindowSystemInterface::handleScreenRemoved(this);
109}
110
111QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
112{
113 return static_cast<QWasmScreen *>(screen);
114}
115
116QWasmScreen *QWasmScreen::get(QScreen *screen)
117{
118 if (!screen)
119 return nullptr;
120 return get(screen->handle());
121}
122
124{
125 return m_compositor.get();
126}
127
129{
130 return m_shadowContainer;
131}
132
133
135{
136 return QString("!outerscreen_%1").arg(uintptr_t(this));
137}
138
139QRect QWasmScreen::geometry() const
140{
141 return m_geometry;
142}
143
144int QWasmScreen::depth() const
145{
146 return m_depth;
147}
148
150{
151 return m_format;
152}
153
155{
156 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
157 if (!dpi.isUndefined()) {
158 qreal dpiValue = dpi.as<qreal>();
159 return QDpi(dpiValue, dpiValue);
160 }
161 const qreal defaultDpi = 96;
162 return QDpi(defaultDpi, defaultDpi);
163}
164
166{
167 // window.devicePixelRatio gives us the scale factor between CSS and device pixels.
168 // This property reflects hardware configuration, and also browser zoom on desktop.
169 //
170 // window.visualViewport.scale gives us the zoom factor on mobile. If the html page is
171 // configured with "<meta name="viewport" content="width=device-width">" then this scale
172 // factor will be 1. Omitting the viewport configuration typically results on a zoomed-out
173 // viewport, with a scale factor <1. User pinch-zoom will change the scale factor; an event
174 // handler is installed in the QWasmIntegration constructor. Changing zoom level on desktop
175 // does not appear to change visualViewport.scale.
176 //
177 // The effective devicePixelRatio is the product of these two scale factors, upper-bounded
178 // by window.devicePixelRatio in order to avoid e.g. allocating a 10x widget backing store.
179 double dpr = emscripten::val::global("window")["devicePixelRatio"].as<double>();
180 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
181 double scale = visualViewport.isUndefined() ? 1.0 : visualViewport["scale"].as<double>();
182 double effectiveDevicePixelRatio = std::min(dpr * scale, dpr);
183 return qreal(effectiveDevicePixelRatio);
184}
185
187{
188 return QString::fromEcmaString(m_shadowContainer["id"]);
189}
190
192{
193 return const_cast<QWasmCursor *>(&m_cursor);
194}
195
197{
198 if (!screen())
199 return;
200 QPlatformScreen::resizeMaximizedWindows();
201}
202
203QWindow *QWasmScreen::topLevelAt(const QPoint &p) const
204{
205 const auto found =
206 std::find_if(childStack().begin(), childStack().end(), [&p](const QWasmWindow *window) {
207 const QRect geometry = window->windowFrameGeometry();
208
209 return window->isVisible() && geometry.contains(p);
210 });
211 return found != childStack().end() ? (*found)->window() : nullptr;
212}
213
214QPointF QWasmScreen::mapFromLocal(const QPointF &p) const
215{
216 return geometry().topLeft() + p;
217}
218
219QPointF QWasmScreen::clipPoint(const QPointF &p) const
220{
221 const auto geometryF = screen()->geometry().toRectF();
222 return QPointF(qBound(geometryF.left(), p.x(), geometryF.right()),
223 qBound(geometryF.top(), p.y(), geometryF.bottom()));
224}
225
227{
228 m_geometry = QRect();
229}
230
231void QWasmScreen::setGeometry(const QRect &rect)
232{
233 m_geometry = rect;
234 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(),
235 availableGeometry());
237}
238
240{
241 double css_width;
242 double css_height;
243 emscripten_get_element_css_size(outerScreenId().toUtf8().constData(), &css_width, &css_height);
244 QSizeF cssSize(css_width, css_height);
245 // A hidden container reports 0x0, keep the last laid out size.
246 if (cssSize.isNull())
247 return;
248
249 // Returns the html elements document/body position
250 auto getElementBodyPosition = [](const emscripten::val &element) -> QPoint {
251 emscripten::val bodyRect =
252 element["ownerDocument"]["body"].call<emscripten::val>("getBoundingClientRect");
253 emscripten::val canvasRect = element.call<emscripten::val>("getBoundingClientRect");
254 return QPoint(canvasRect["left"].as<int>() - bodyRect["left"].as<int>(),
255 canvasRect["top"].as<int>() - bodyRect["top"].as<int>());
256 };
257
258 setGeometry(QRect(getElementBodyPosition(m_shadowContainer), cssSize.toSize()));
259}
260
261// Tracks container layout changes the window resize event misses, such as the
262// container being revealed or sized by the page after Qt has started.
264{
265 QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
266 m_resizeObserverHandlerIndex = suspendResume->registerEventHandler(
267 [this](emscripten::val) { updateQScreenSize(); });
268 m_resizeObserver = emscripten::val::global("ResizeObserver").new_(
269 suspendResume->jsEventHandlerAt(m_resizeObserverHandlerIndex));
270 m_resizeObserver.call<void>("observe", m_shadowContainer);
271}
272
274{
275 return m_shadowContainer;
276}
277
279{
280 return nullptr;
281}
282
284{
285 QList<QWasmWindow *> currentChildren;
286 QList<QWasmWindow *> result;
287
288 for (auto *w : childStack())
289 currentChildren << w;
290
291 while (!currentChildren.empty()) {
292 result << currentChildren;
293
294 QList<QWasmWindow *> toIterate;
295 currentChildren.swap(toIterate);
296
297 for (auto child : toIterate) {
298 for (auto *w : child->childStack())
299 currentChildren << w;
300 }
301 }
302 return result;
303}
304
305QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:30
QWasmCompositor(QWasmScreen *screen)
QString name() const override
QList< QWasmWindow * > allWindows() const
void installCanvasResizeObserver()
emscripten::val element() const
QPointF mapFromLocal(const QPointF &p) const
void deleteScreen()
QPointF clipPoint(const QPointF &p) const
QWasmCompositor * compositor()
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.
void resizeMaximizedWindows()
void invalidateSize()
QString outerScreenId() const
int depth() const override
Reimplement in subclass to return current depth of the screen.
QDpi logicalDpi() const override
Reimplement this function in subclass to return the logical horizontal and vertical dots per inch met...
void updateQScreenSize()
emscripten::val containerElement() final
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
Combined button and popup list for selecting options.