1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylandscreen_p.h"
5
6#include "qwaylanddisplay_p.h"
7#include "qwaylandintegration_p.h"
8#include "qwaylandcursor_p.h"
9#include "qwaylandwindow_p.h"
10
11#include <QtGui/QGuiApplication>
12
13#include <qpa/qwindowsysteminterface.h>
14#include <qpa/qplatformwindow.h>
15
16QT_BEGIN_NAMESPACE
17
18namespace QtWaylandClient {
19
20QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, uint id, uint version)
21 : QtWayland::zxdg_output_manager_v1(display->wl_registry(), id, qMin(3u, version))
22{
23}
24
25QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
26 : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3))
27 , m_outputId(id)
28 , mWaylandDisplay(waylandDisplay)
29 , mOutputName(QStringLiteral("Screen%1").arg(a: id))
30{
31 if (auto *xdgOutputManager = waylandDisplay->xdgOutputManager())
32 initXdgOutput(xdgOutputManager);
33
34 if (version < WL_OUTPUT_DONE_SINCE_VERSION) {
35 qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
36 << "QScreen may not work correctly";
37 mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
38 mProcessedEvents |= OutputDoneEvent; // Fake the done event
39 maybeInitialize();
40 }
41}
42
43QWaylandScreen::~QWaylandScreen()
44{
45 if (zxdg_output_v1::isInitialized())
46 zxdg_output_v1::destroy();
47 if (wl_output::isInitialized() && wl_output::version() >= WL_OUTPUT_RELEASE_SINCE_VERSION)
48 wl_output::release();
49}
50
51uint QWaylandScreen::requiredEvents() const
52{
53 uint ret = OutputDoneEvent;
54
55 if (mWaylandDisplay->xdgOutputManager()) {
56 if (mWaylandDisplay->xdgOutputManager()->version() >= 2)
57 ret |= XdgOutputNameEvent;
58
59 if (mWaylandDisplay->xdgOutputManager()->version() < 3)
60 ret |= XdgOutputDoneEvent;
61 }
62 return ret;
63}
64
65void QWaylandScreen::maybeInitialize()
66{
67 Q_ASSERT(!mInitialized);
68
69 const uint requiredEvents = this->requiredEvents();
70 if ((mProcessedEvents & requiredEvents) != requiredEvents)
71 return;
72
73 mInitialized = true;
74 mWaylandDisplay->handleScreenInitialized(screen: this);
75
76 updateOutputProperties();
77 if (zxdg_output_v1::isInitialized())
78 updateXdgOutputProperties();
79}
80
81void QWaylandScreen::initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager)
82{
83 Q_ASSERT(xdgOutputManager);
84 if (zxdg_output_v1::isInitialized())
85 return;
86
87 zxdg_output_v1::init(xdgOutputManager->get_xdg_output(wl_output::object()));
88}
89
90QWaylandDisplay * QWaylandScreen::display() const
91{
92 return mWaylandDisplay;
93}
94
95QString QWaylandScreen::manufacturer() const
96{
97 return mManufacturer;
98}
99
100QString QWaylandScreen::model() const
101{
102 return mModel;
103}
104
105QRect QWaylandScreen::geometry() const
106{
107 if (zxdg_output_v1::isInitialized()) {
108 return mXdgGeometry;
109 } else {
110 // Scale geometry for QScreen. This makes window and screen
111 // geometry be in the same coordinate system.
112 return QRect(mGeometry.topLeft(), mGeometry.size() / mScale);
113 }
114}
115
116int QWaylandScreen::depth() const
117{
118 return mDepth;
119}
120
121QImage::Format QWaylandScreen::format() const
122{
123 return mFormat;
124}
125
126QSizeF QWaylandScreen::physicalSize() const
127{
128 if (mPhysicalSize.isEmpty())
129 return QPlatformScreen::physicalSize();
130 else
131 return mPhysicalSize;
132}
133
134QDpi QWaylandScreen::logicalDpi() const
135{
136 static bool physicalDpi = qEnvironmentVariable(varName: "QT_WAYLAND_FORCE_DPI") == QStringLiteral("physical");
137 if (physicalDpi)
138 return QPlatformScreen::logicalDpi();
139
140 static int forceDpi = qgetenv(varName: "QT_WAYLAND_FORCE_DPI").toInt();
141 if (forceDpi)
142 return QDpi(forceDpi, forceDpi);
143
144 return QDpi(96, 96);
145}
146
147QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
148{
149 QList<QPlatformScreen *> list;
150 const QList<QWaylandScreen*> screens = mWaylandDisplay->screens();
151 auto *placeholder = mWaylandDisplay->placeholderScreen();
152
153 list.reserve(asize: screens.size() + (placeholder ? 1 : 0));
154
155 for (QWaylandScreen *screen : std::as_const(t: screens)) {
156 if (screen->screen())
157 list << screen;
158 }
159
160 if (placeholder)
161 list << placeholder;
162
163 return list;
164}
165
166Qt::ScreenOrientation QWaylandScreen::orientation() const
167{
168 return m_orientation;
169}
170
171int QWaylandScreen::scale() const
172{
173 return mScale;
174}
175
176qreal QWaylandScreen::devicePixelRatio() const
177{
178 return qreal(mScale);
179}
180
181qreal QWaylandScreen::refreshRate() const
182{
183 return mRefreshRate / 1000.f;
184}
185
186#if QT_CONFIG(cursor)
187QPlatformCursor *QWaylandScreen::cursor() const
188{
189 return mWaylandDisplay->waylandCursor();
190}
191#endif // QT_CONFIG(cursor)
192
193QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window)
194{
195 QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window);
196 if (platformScreen->isPlaceholder())
197 return nullptr;
198 return static_cast<QWaylandScreen *>(platformScreen);
199}
200
201QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
202{
203 if (auto *o = QtWayland::wl_output::fromObject(output))
204 return static_cast<QWaylandScreen *>(o);
205 return nullptr;
206}
207
208void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)
209{
210 if (!(flags & WL_OUTPUT_MODE_CURRENT))
211 return;
212
213 QSize size(width, height);
214 if (size != mGeometry.size())
215 mGeometry.setSize(size);
216
217 if (refresh != mRefreshRate)
218 mRefreshRate = refresh;
219}
220
221void QWaylandScreen::output_geometry(int32_t x, int32_t y,
222 int32_t width, int32_t height,
223 int subpixel,
224 const QString &make,
225 const QString &model,
226 int32_t transform)
227{
228 Q_UNUSED(subpixel);
229
230 mManufacturer = make;
231 mModel = model;
232
233 mTransform = transform;
234
235 mPhysicalSize = QSize(width, height);
236 mGeometry.moveTopLeft(p: QPoint(x, y));
237}
238
239void QWaylandScreen::output_scale(int32_t factor)
240{
241 mScale = factor;
242}
243
244void QWaylandScreen::output_done()
245{
246 mProcessedEvents |= OutputDoneEvent;
247
248 if (mInitialized) {
249 updateOutputProperties();
250 if (zxdg_output_v1::isInitialized())
251 updateXdgOutputProperties();
252 } else {
253 maybeInitialize();
254 }
255}
256
257void QWaylandScreen::updateOutputProperties()
258{
259 if (mTransform >= 0) {
260 bool isPortrait = mGeometry.height() > mGeometry.width();
261 switch (mTransform) {
262 case WL_OUTPUT_TRANSFORM_NORMAL:
263 m_orientation = isPortrait ? Qt::PortraitOrientation : Qt::LandscapeOrientation;
264 break;
265 case WL_OUTPUT_TRANSFORM_90:
266 m_orientation = isPortrait ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation;
267 break;
268 case WL_OUTPUT_TRANSFORM_180:
269 m_orientation = isPortrait ? Qt::InvertedPortraitOrientation : Qt::InvertedLandscapeOrientation;
270 break;
271 case WL_OUTPUT_TRANSFORM_270:
272 m_orientation = isPortrait ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation;
273 break;
274 // Ignore these ones, at least for now
275 case WL_OUTPUT_TRANSFORM_FLIPPED:
276 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
277 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
278 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
279 break;
280 }
281
282 QWindowSystemInterface::handleScreenOrientationChange(screen: screen(), newOrientation: m_orientation);
283 mTransform = -1;
284 }
285
286 QWindowSystemInterface::handleScreenRefreshRateChange(screen: screen(), newRefreshRate: refreshRate());
287
288 if (!zxdg_output_v1::isInitialized())
289 QWindowSystemInterface::handleScreenGeometryChange(screen: screen(), newGeometry: geometry(), newAvailableGeometry: geometry());
290}
291
292
293void QWaylandScreen::zxdg_output_v1_logical_position(int32_t x, int32_t y)
294{
295 mXdgGeometry.moveTopLeft(p: QPoint(x, y));
296}
297
298void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height)
299{
300 mXdgGeometry.setSize(QSize(width, height));
301}
302
303void QWaylandScreen::zxdg_output_v1_done()
304{
305 if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3))
306 qWarning(catFunc: lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor";
307
308 mProcessedEvents |= XdgOutputDoneEvent;
309 if (mInitialized)
310 updateXdgOutputProperties();
311 else
312 maybeInitialize();
313}
314
315void QWaylandScreen::zxdg_output_v1_name(const QString &name)
316{
317 if (Q_UNLIKELY(mInitialized))
318 qWarning(catFunc: lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor";
319
320 mOutputName = name;
321 mProcessedEvents |= XdgOutputNameEvent;
322}
323
324void QWaylandScreen::updateXdgOutputProperties()
325{
326 Q_ASSERT(zxdg_output_v1::isInitialized());
327 QWindowSystemInterface::handleScreenGeometryChange(screen: screen(), newGeometry: geometry(), newAvailableGeometry: geometry());
328}
329
330} // namespace QtWaylandClient
331
332QT_END_NAMESPACE
333

source code of qtwayland/src/client/qwaylandscreen.cpp