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
4#include "qwasmscreen.h"
5
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{
35 auto document = m_container["ownerDocument"];
36
37 // Create an intermediate container which we can remove during cleanup in ~QWasmScreen().
38 // This is required due to the attachShadow() call below; there is no corresponding
39 // "detachShadow()" API to return the container to its previous state.
40 m_intermediateContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
41 m_intermediateContainer.set("id", std::string("qt-shadow-container"));
42 emscripten::val intermediateContainerStyle = m_intermediateContainer["style"];
43 intermediateContainerStyle.set("width", std::string("100%"));
44 intermediateContainerStyle.set("height", std::string("100%"));
45 m_container.call<void>("appendChild", m_intermediateContainer);
46 // Each screen is represented by a div container. All of the windows exist therein as
47 // its children. Qt versions < 6.5 used to represent screens as canvas elements; this
48 // is no longer supported.
49 if (m_container["tagName"].call<std::string>("toLowerCase") == "canvas")
50 qFatal() << "Qt does not support using a canvas element as the container element. Use a div element instead";
51
52 auto shadowOptions = emscripten::val::object();
53 shadowOptions.set("mode", "open");
54 auto shadow = m_intermediateContainer.call<emscripten::val>("attachShadow", shadowOptions);
55
56 m_shadowContainer = document.call<emscripten::val>("createElement", emscripten::val("div"));
57
58 shadow.call<void>("appendChild", QWasmCSSStyle::createStyleElement(m_shadowContainer));
59
60 shadow.call<void>("appendChild", m_shadowContainer);
61
62 m_shadowContainer.set("id", std::string("qt-screen-") + std::to_string(uintptr_t(this)));
63
64 m_shadowContainer["classList"].call<void>("add", std::string("qt-screen"));
65
66 // Disable the default context menu; Qt applications typically
67 // provide custom right-click behavior.
68 m_onContextMenu = std::make_unique<qstdweb::EventCallback>(
69 m_shadowContainer, "contextmenu",
70 [](emscripten::val event) { event.call<void>("preventDefault"); });
71 // Create "specialHTMLTargets" mapping for the canvas - the element might be unreachable based
72 // on its id only under some conditions, like the target being embedded in a shadow DOM or a
73 // subframe.
74 emscripten::val::module_property("specialHTMLTargets")
75 .set(outerScreenId().toStdString(), m_container);
76
78 m_shadowContainer.call<void>("focus");
79
80 m_touchDevice = std::make_unique<QPointingDevice>(
81 "touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
82 QPointingDevice::PointerType::Finger,
83 QPointingDevice::Capability::Position | QPointingDevice::Capability::Area
84 | QPointingDevice::Capability::NormalizedPosition,
85 10, 0);
86 m_tabletDevice = std::make_unique<QPointingDevice>(
87 "stylus", 2, QInputDevice::DeviceType::Stylus,
88 QPointingDevice::PointerType::Pen,
89 QPointingDevice::Capability::Position | QPointingDevice::Capability::Pressure
90 | QPointingDevice::Capability::NormalizedPosition
91 | QInputDevice::Capability::MouseEmulation
92 | QInputDevice::Capability::Hover | QInputDevice::Capability::Rotation
93 | QInputDevice::Capability::XTilt | QInputDevice::Capability::YTilt
94 | QInputDevice::Capability::TangentialPressure,
95 0, 0);
96
97 QWindowSystemInterface::registerInputDevice(m_touchDevice.get());
98}
99
101{
102 m_intermediateContainer.call<void>("remove");
103
104 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
105 emscripten::val(intptr_t(0)));
106}
107
109{
110 // Deletes |this|!
111 QWindowSystemInterface::handleScreenRemoved(this);
112}
113
114QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
115{
116 return static_cast<QWasmScreen *>(screen);
117}
118
119QWasmScreen *QWasmScreen::get(QScreen *screen)
120{
121 if (!screen)
122 return nullptr;
123 return get(screen->handle());
124}
125
127{
128 return m_compositor.get();
129}
130
132{
133 return m_shadowContainer;
134}
135
136
138{
139 return QString("!outerscreen_%1").arg(uintptr_t(this));
140}
141
142QRect QWasmScreen::geometry() const
143{
144 return m_geometry;
145}
146
147int QWasmScreen::depth() const
148{
149 return m_depth;
150}
151
153{
154 return m_format;
155}
156
158{
159 emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
160 if (!dpi.isUndefined()) {
161 qreal dpiValue = dpi.as<qreal>();
162 return QDpi(dpiValue, dpiValue);
163 }
164 const qreal defaultDpi = 96;
165 return QDpi(defaultDpi, defaultDpi);
166}
167
169{
170 // window.devicePixelRatio gives us the scale factor between CSS and device pixels.
171 // This property reflects hardware configuration, and also browser zoom on desktop.
172 //
173 // window.visualViewport.scale gives us the zoom factor on mobile. If the html page is
174 // configured with "<meta name="viewport" content="width=device-width">" then this scale
175 // factor will be 1. Omitting the viewport configuration typically results on a zoomed-out
176 // viewport, with a scale factor <1. User pinch-zoom will change the scale factor; an event
177 // handler is installed in the QWasmIntegration constructor. Changing zoom level on desktop
178 // does not appear to change visualViewport.scale.
179 //
180 // The effective devicePixelRatio is the product of these two scale factors, upper-bounded
181 // by window.devicePixelRatio in order to avoid e.g. allocating a 10x widget backing store.
182 double dpr = emscripten::val::global("window")["devicePixelRatio"].as<double>();
183 emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
184 double scale = visualViewport.isUndefined() ? 1.0 : visualViewport["scale"].as<double>();
185 double effectiveDevicePixelRatio = std::min(dpr * scale, dpr);
186 return qreal(effectiveDevicePixelRatio);
187}
188
190{
191 return QString::fromEcmaString(m_shadowContainer["id"]);
192}
193
195{
196 return const_cast<QWasmCursor *>(&m_cursor);
197}
198
200{
201 if (!screen())
202 return;
203 QPlatformScreen::resizeMaximizedWindows();
204}
205
206QWindow *QWasmScreen::topWindow() const
207{
208 return activeChild() ? activeChild()->window() : nullptr;
209}
210
211QWindow *QWasmScreen::topLevelAt(const QPoint &p) const
212{
213 const auto found =
214 std::find_if(childStack().begin(), childStack().end(), [&p](const QWasmWindow *window) {
215 const QRect geometry = window->windowFrameGeometry();
216
217 return window->isVisible() && geometry.contains(p);
218 });
219 return found != childStack().end() ? (*found)->window() : nullptr;
220}
221
222QPointF QWasmScreen::mapFromLocal(const QPointF &p) const
223{
224 return geometry().topLeft() + p;
225}
226
227QPointF QWasmScreen::clipPoint(const QPointF &p) const
228{
229 const auto geometryF = screen()->geometry().toRectF();
230 return QPointF(qBound(geometryF.left(), p.x(), geometryF.right()),
231 qBound(geometryF.top(), p.y(), geometryF.bottom()));
232}
233
235{
236 m_geometry = QRect();
237}
238
239void QWasmScreen::setGeometry(const QRect &rect)
240{
241 m_geometry = rect;
242 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(),
243 availableGeometry());
245}
246
248 QWasmWindowTreeNode *parent, QWasmWindow *child)
249{
250 Q_UNUSED(parent);
251
252 QWindow *window = child->window();
253 const bool isMaxFull = (window->windowState() & Qt::WindowMaximized) ||
254 (window->windowState() & Qt::WindowFullScreen);
255 if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
256 && childStack().size() == 1 && isMaxFull) {
257 window->setFlag(Qt::WindowStaysOnBottomHint);
258 }
259 QWasmWindowTreeNode::onSubtreeChanged(changeType, parent, child);
260 m_compositor->onWindowTreeChanged(changeType, child);
261}
262
264{
265 double css_width;
266 double css_height;
267 emscripten_get_element_css_size(outerScreenId().toUtf8().constData(), &css_width, &css_height);
268 QSizeF cssSize(css_width, css_height);
269
270 // Returns the html elements document/body position
271 auto getElementBodyPosition = [](const emscripten::val &element) -> QPoint {
272 emscripten::val bodyRect =
273 element["ownerDocument"]["body"].call<emscripten::val>("getBoundingClientRect");
274 emscripten::val canvasRect = element.call<emscripten::val>("getBoundingClientRect");
275 return QPoint(canvasRect["left"].as<int>() - bodyRect["left"].as<int>(),
276 canvasRect["top"].as<int>() - bodyRect["top"].as<int>());
277 };
278
279 setGeometry(QRect(getElementBodyPosition(m_shadowContainer), cssSize.toSize()));
280}
281
283{
284 int count = entries["length"].as<int>();
285 if (count == 0)
286 return;
287 emscripten::val entry = entries[0];
288 QWasmScreen *screen = reinterpret_cast<QWasmScreen *>(
289 entry["target"][m_canvasResizeObserverCallbackContextPropertyName].as<intptr_t>());
290 if (!screen) {
291 qWarning() << "QWasmScreen::canvasResizeObserverCallback: missing screen pointer";
292 return;
293 }
294
296}
297
299{
300 emscripten::function("qtCanvasResizeObserverCallback",
301 &QWasmScreen::canvasResizeObserverCallback);
302}
303
305{
306 emscripten::val ResizeObserver = emscripten::val::global("ResizeObserver");
307 if (ResizeObserver == emscripten::val::undefined())
308 return; // ResizeObserver API is not available
309 emscripten::val resizeObserver =
310 ResizeObserver.new_(emscripten::val::module_property("qtCanvasResizeObserverCallback"));
311 if (resizeObserver == emscripten::val::undefined())
312 return; // Something went horribly wrong
313
314 // We need to get back to this instance from the (static) resize callback;
315 // set a "data-" property on the canvas element.
316 m_shadowContainer.set(m_canvasResizeObserverCallbackContextPropertyName,
317 emscripten::val(intptr_t(this)));
318
319 resizeObserver.call<void>("observe", m_shadowContainer);
320}
321
323{
324 return m_shadowContainer;
325}
326
328{
329 return nullptr;
330}
331
333{
334 QList<QWasmWindow *> currentChildren;
335 QList<QWasmWindow *> result;
336
337 for (auto *w : childStack())
338 currentChildren << w;
339
340 while (!currentChildren.empty()) {
341 result << currentChildren;
342
343 QList<QWasmWindow *> toIterate;
344 currentChildren.swap(toIterate);
345
346 for (auto child : toIterate) {
347 for (auto *w : child->childStack())
348 currentChildren << w;
349 }
350 }
351 return result;
352}
353
354QT_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 onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, QWasmWindow *child) final
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...
void updateQScreenSize()
static void canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
emscripten::val containerElement() final
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
friend class QWasmCompositor
Combined button and popup list for selecting options.
EMSCRIPTEN_BINDINGS(qtCanvasResizeObserverCallback)
QWasmWindowTreeNodeChangeType