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
25QWaylandXdgOutputManagerV1::~QWaylandXdgOutputManagerV1()
26{
27 destroy();
28}
29
30QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
31 : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3))
32 , m_outputId(id)
33 , mWaylandDisplay(waylandDisplay)
34 , mOutputName(QStringLiteral("Screen%1").arg(a: id))
35{
36 if (auto *xdgOutputManager = waylandDisplay->xdgOutputManager())
37 initXdgOutput(xdgOutputManager);
38
39 if (version < WL_OUTPUT_DONE_SINCE_VERSION) {
40 qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
41 << "QScreen may not work correctly";
42 mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
43 mProcessedEvents |= OutputDoneEvent; // Fake the done event
44 maybeInitialize();
45 }
46}
47
48QWaylandScreen::~QWaylandScreen()
49{
50 if (zxdg_output_v1::isInitialized())
51 zxdg_output_v1::destroy();
52 if (wl_output::version() >= WL_OUTPUT_RELEASE_SINCE_VERSION)
53 wl_output::release();
54 else
55 wl_output_destroy(wl_output::object());
56}
57
58uint QWaylandScreen::requiredEvents() const
59{
60 uint ret = OutputDoneEvent;
61
62 if (mWaylandDisplay->xdgOutputManager()) {
63 if (mWaylandDisplay->xdgOutputManager()->version() >= 2)
64 ret |= XdgOutputNameEvent;
65
66 if (mWaylandDisplay->xdgOutputManager()->version() < 3)
67 ret |= XdgOutputDoneEvent;
68 }
69 return ret;
70}
71
72void QWaylandScreen::maybeInitialize()
73{
74 Q_ASSERT(!mInitialized);
75
76 const uint requiredEvents = this->requiredEvents();
77 if ((mProcessedEvents & requiredEvents) != requiredEvents)
78 return;
79
80 mInitialized = true;
81 mWaylandDisplay->handleScreenInitialized(screen: this);
82
83 updateOutputProperties();
84 if (zxdg_output_v1::isInitialized())
85 updateXdgOutputProperties();
86}
87
88void QWaylandScreen::initXdgOutput(QWaylandXdgOutputManagerV1 *xdgOutputManager)
89{
90 Q_ASSERT(xdgOutputManager);
91 if (zxdg_output_v1::isInitialized())
92 return;
93
94 zxdg_output_v1::init(xdgOutputManager->get_xdg_output(wl_output::object()));
95}
96
97QWaylandDisplay * QWaylandScreen::display() const
98{
99 return mWaylandDisplay;
100}
101
102QString QWaylandScreen::manufacturer() const
103{
104 return mManufacturer;
105}
106
107QString QWaylandScreen::model() const
108{
109 return mModel;
110}
111
112QRect QWaylandScreen::geometry() const
113{
114 if (zxdg_output_v1::isInitialized()) {
115
116 // Workaround for Gnome bug
117 // https://gitlab.gnome.org/GNOME/mutter/-/issues/2631
118 // which sends an incorrect xdg geometry
119 const bool xdgGeometryIsBogus = mScale > 1 && mXdgGeometry.size() == mGeometry.size();
120
121 if (!xdgGeometryIsBogus) {
122 return mXdgGeometry;
123 }
124 }
125 // Scale geometry for QScreen. This makes window and screen
126 // geometry be in the same coordinate system.
127 return QRect(mGeometry.topLeft(), mGeometry.size() / mScale);
128}
129
130int QWaylandScreen::depth() const
131{
132 return mDepth;
133}
134
135QImage::Format QWaylandScreen::format() const
136{
137 return mFormat;
138}
139
140QSizeF QWaylandScreen::physicalSize() const
141{
142 if (mPhysicalSize.isEmpty())
143 return QPlatformScreen::physicalSize();
144 else
145 return mPhysicalSize;
146}
147
148QDpi QWaylandScreen::logicalDpi() const
149{
150 static bool physicalDpi = qEnvironmentVariable(varName: "QT_WAYLAND_FORCE_DPI") == QStringLiteral("physical");
151 if (physicalDpi)
152 return QPlatformScreen::logicalDpi();
153
154 static int forceDpi = qgetenv(varName: "QT_WAYLAND_FORCE_DPI").toInt();
155 if (forceDpi)
156 return QDpi(forceDpi, forceDpi);
157
158 return QDpi(96, 96);
159}
160
161QList<QPlatformScreen *> QWaylandScreen::virtualSiblings() const
162{
163 QList<QPlatformScreen *> list;
164 const QList<QWaylandScreen*> screens = mWaylandDisplay->screens();
165 auto *placeholder = mWaylandDisplay->placeholderScreen();
166
167 list.reserve(asize: screens.size() + (placeholder ? 1 : 0));
168
169 for (QWaylandScreen *screen : std::as_const(t: screens)) {
170 if (screen->screen())
171 list << screen;
172 }
173
174 if (placeholder)
175 list << placeholder;
176
177 return list;
178}
179
180Qt::ScreenOrientation QWaylandScreen::orientation() const
181{
182 return m_orientation;
183}
184
185int QWaylandScreen::scale() const
186{
187 return mScale;
188}
189
190qreal QWaylandScreen::devicePixelRatio() const
191{
192 return qreal(mScale);
193}
194
195qreal QWaylandScreen::refreshRate() const
196{
197 return mRefreshRate / 1000.f;
198}
199
200#if QT_CONFIG(cursor)
201QPlatformCursor *QWaylandScreen::cursor() const
202{
203 return mWaylandDisplay->waylandCursor();
204}
205#endif // QT_CONFIG(cursor)
206
207QPlatformScreen::SubpixelAntialiasingType QWaylandScreen::subpixelAntialiasingTypeHint() const
208{
209 QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
210 if (type == QPlatformScreen::Subpixel_None) {
211 switch (mSubpixel) {
212 case wl_output::subpixel_unknown:
213 case wl_output::subpixel_none:
214 type = QPlatformScreen::Subpixel_None;
215 break;
216 case wl_output::subpixel_horizontal_rgb:
217 type = QPlatformScreen::Subpixel_RGB;
218 break;
219 case wl_output::subpixel_horizontal_bgr:
220 type = QPlatformScreen::Subpixel_BGR;
221 break;
222 case wl_output::subpixel_vertical_rgb:
223 type = QPlatformScreen::Subpixel_VRGB;
224 break;
225 case wl_output::subpixel_vertical_bgr:
226 type = QPlatformScreen::Subpixel_VBGR;
227 break;
228 }
229 }
230 return type;
231}
232
233QWaylandScreen *QWaylandScreen::waylandScreenFromWindow(QWindow *window)
234{
235 QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(window);
236 if (platformScreen->isPlaceholder())
237 return nullptr;
238 return static_cast<QWaylandScreen *>(platformScreen);
239}
240
241QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
242{
243 if (auto *o = QtWayland::wl_output::fromObject(output))
244 return static_cast<QWaylandScreen *>(o);
245 return nullptr;
246}
247
248Qt::ScreenOrientation QWaylandScreen::toScreenOrientation(int wlTransform,
249 Qt::ScreenOrientation fallback) const
250{
251 auto orientation = fallback;
252 bool isPortrait = mGeometry.height() > mGeometry.width();
253 switch (wlTransform) {
254 case WL_OUTPUT_TRANSFORM_NORMAL:
255 orientation = isPortrait ? Qt::PortraitOrientation : Qt::LandscapeOrientation;
256 break;
257 case WL_OUTPUT_TRANSFORM_90:
258 orientation = isPortrait ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation;
259 break;
260 case WL_OUTPUT_TRANSFORM_180:
261 orientation = isPortrait ? Qt::InvertedPortraitOrientation : Qt::InvertedLandscapeOrientation;
262 break;
263 case WL_OUTPUT_TRANSFORM_270:
264 orientation = isPortrait ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation;
265 break;
266 // Ignore these ones, at least for now
267 case WL_OUTPUT_TRANSFORM_FLIPPED:
268 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
269 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
270 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
271 break;
272 }
273
274 return orientation;
275}
276
277void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)
278{
279 if (!(flags & WL_OUTPUT_MODE_CURRENT))
280 return;
281
282 QSize size(width, height);
283 if (size != mGeometry.size())
284 mGeometry.setSize(size);
285
286 if (refresh != mRefreshRate)
287 mRefreshRate = refresh;
288}
289
290void QWaylandScreen::output_geometry(int32_t x, int32_t y,
291 int32_t width, int32_t height,
292 int subpixel,
293 const QString &make,
294 const QString &model,
295 int32_t transform)
296{
297 mManufacturer = make;
298 mModel = model;
299
300 mSubpixel = subpixel;
301 mTransform = transform;
302
303 mPhysicalSize = QSize(width, height);
304 mGeometry.moveTopLeft(p: QPoint(x, y));
305}
306
307void QWaylandScreen::output_scale(int32_t factor)
308{
309 mScale = factor;
310}
311
312void QWaylandScreen::output_done()
313{
314 mProcessedEvents |= OutputDoneEvent;
315
316 if (mInitialized) {
317 updateOutputProperties();
318 if (zxdg_output_v1::isInitialized())
319 updateXdgOutputProperties();
320 } else {
321 maybeInitialize();
322 }
323}
324
325void QWaylandScreen::updateOutputProperties()
326{
327 if (mTransform >= 0) {
328 auto newOrientation = toScreenOrientation(wlTransform: mTransform, fallback: m_orientation);
329 if (m_orientation != newOrientation) {
330 m_orientation = newOrientation;
331 QWindowSystemInterface::handleScreenOrientationChange(screen: screen(), newOrientation: m_orientation);
332 }
333 mTransform = -1;
334 }
335
336 QWindowSystemInterface::handleScreenRefreshRateChange(screen: screen(), newRefreshRate: refreshRate());
337
338 if (!zxdg_output_v1::isInitialized())
339 QWindowSystemInterface::handleScreenGeometryChange(screen: screen(), newGeometry: geometry(), newAvailableGeometry: geometry());
340}
341
342
343void QWaylandScreen::zxdg_output_v1_logical_position(int32_t x, int32_t y)
344{
345 mXdgGeometry.moveTopLeft(p: QPoint(x, y));
346}
347
348void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height)
349{
350 mXdgGeometry.setSize(QSize(width, height));
351}
352
353void QWaylandScreen::zxdg_output_v1_done()
354{
355 if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3))
356 qCWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor";
357
358 mProcessedEvents |= XdgOutputDoneEvent;
359 if (mInitialized)
360 updateXdgOutputProperties();
361 else
362 maybeInitialize();
363}
364
365void QWaylandScreen::zxdg_output_v1_name(const QString &name)
366{
367 if (Q_UNLIKELY(mInitialized))
368 qCWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor";
369
370 mOutputName = name;
371 mProcessedEvents |= XdgOutputNameEvent;
372}
373
374void QWaylandScreen::updateXdgOutputProperties()
375{
376 Q_ASSERT(zxdg_output_v1::isInitialized());
377 QWindowSystemInterface::handleScreenGeometryChange(screen: screen(), newGeometry: geometry(), newAvailableGeometry: geometry());
378}
379
380} // namespace QtWaylandClient
381
382QT_END_NAMESPACE
383

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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