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 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | namespace QtWaylandClient { |
19 | |
20 | QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, uint id, uint version) |
21 | : QtWayland::zxdg_output_manager_v1(display->wl_registry(), id, qMin(3u, version)) |
22 | { |
23 | } |
24 | |
25 | QWaylandXdgOutputManagerV1::~QWaylandXdgOutputManagerV1() |
26 | { |
27 | destroy(); |
28 | } |
29 | |
30 | QWaylandScreen::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 | |
48 | QWaylandScreen::~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 | |
58 | uint 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 | |
72 | void 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 | |
88 | void 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 | |
97 | QWaylandDisplay * QWaylandScreen::display() const |
98 | { |
99 | return mWaylandDisplay; |
100 | } |
101 | |
102 | QString QWaylandScreen::manufacturer() const |
103 | { |
104 | return mManufacturer; |
105 | } |
106 | |
107 | QString QWaylandScreen::model() const |
108 | { |
109 | return mModel; |
110 | } |
111 | |
112 | QRect 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 | |
130 | int QWaylandScreen::depth() const |
131 | { |
132 | return mDepth; |
133 | } |
134 | |
135 | QImage::Format QWaylandScreen::format() const |
136 | { |
137 | return mFormat; |
138 | } |
139 | |
140 | QSizeF QWaylandScreen::physicalSize() const |
141 | { |
142 | if (mPhysicalSize.isEmpty()) |
143 | return QPlatformScreen::physicalSize(); |
144 | else |
145 | return mPhysicalSize; |
146 | } |
147 | |
148 | QDpi 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 | |
161 | QList<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 | |
180 | Qt::ScreenOrientation QWaylandScreen::orientation() const |
181 | { |
182 | return m_orientation; |
183 | } |
184 | |
185 | int QWaylandScreen::scale() const |
186 | { |
187 | return mScale; |
188 | } |
189 | |
190 | qreal QWaylandScreen::devicePixelRatio() const |
191 | { |
192 | return qreal(mScale); |
193 | } |
194 | |
195 | qreal QWaylandScreen::refreshRate() const |
196 | { |
197 | return mRefreshRate / 1000.f; |
198 | } |
199 | |
200 | #if QT_CONFIG(cursor) |
201 | QPlatformCursor *QWaylandScreen::cursor() const |
202 | { |
203 | return mWaylandDisplay->waylandCursor(); |
204 | } |
205 | #endif // QT_CONFIG(cursor) |
206 | |
207 | QPlatformScreen::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 | |
233 | QWaylandScreen *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 | |
241 | QWaylandScreen *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 | |
248 | Qt::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 | |
277 | void 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 | |
290 | void 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 | |
307 | void QWaylandScreen::output_scale(int32_t factor) |
308 | { |
309 | mScale = factor; |
310 | } |
311 | |
312 | void 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 | |
325 | void 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 | |
343 | void QWaylandScreen::zxdg_output_v1_logical_position(int32_t x, int32_t y) |
344 | { |
345 | mXdgGeometry.moveTopLeft(p: QPoint(x, y)); |
346 | } |
347 | |
348 | void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height) |
349 | { |
350 | mXdgGeometry.setSize(QSize(width, height)); |
351 | } |
352 | |
353 | void 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 | |
365 | void 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 | |
374 | void 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 | |
382 | QT_END_NAMESPACE |
383 |
Definitions
- QWaylandXdgOutputManagerV1
- ~QWaylandXdgOutputManagerV1
- QWaylandScreen
- ~QWaylandScreen
- requiredEvents
- maybeInitialize
- initXdgOutput
- display
- manufacturer
- model
- geometry
- depth
- format
- physicalSize
- logicalDpi
- virtualSiblings
- orientation
- scale
- devicePixelRatio
- refreshRate
- cursor
- subpixelAntialiasingTypeHint
- waylandScreenFromWindow
- fromWlOutput
- toScreenOrientation
- output_mode
- output_geometry
- output_scale
- output_done
- updateOutputProperties
- zxdg_output_v1_logical_position
- zxdg_output_v1_logical_size
- zxdg_output_v1_done
- zxdg_output_v1_name
Learn Advanced QML with KDAB
Find out more