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 | QWaylandScreen::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 | |
43 | QWaylandScreen::~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 | |
51 | uint 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 | |
65 | void 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 | |
81 | void 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 | |
90 | QWaylandDisplay * QWaylandScreen::display() const |
91 | { |
92 | return mWaylandDisplay; |
93 | } |
94 | |
95 | QString QWaylandScreen::manufacturer() const |
96 | { |
97 | return mManufacturer; |
98 | } |
99 | |
100 | QString QWaylandScreen::model() const |
101 | { |
102 | return mModel; |
103 | } |
104 | |
105 | QRect 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 | |
116 | int QWaylandScreen::depth() const |
117 | { |
118 | return mDepth; |
119 | } |
120 | |
121 | QImage::Format QWaylandScreen::format() const |
122 | { |
123 | return mFormat; |
124 | } |
125 | |
126 | QSizeF QWaylandScreen::physicalSize() const |
127 | { |
128 | if (mPhysicalSize.isEmpty()) |
129 | return QPlatformScreen::physicalSize(); |
130 | else |
131 | return mPhysicalSize; |
132 | } |
133 | |
134 | QDpi 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 | |
147 | QList<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 | |
166 | Qt::ScreenOrientation QWaylandScreen::orientation() const |
167 | { |
168 | return m_orientation; |
169 | } |
170 | |
171 | int QWaylandScreen::scale() const |
172 | { |
173 | return mScale; |
174 | } |
175 | |
176 | qreal QWaylandScreen::devicePixelRatio() const |
177 | { |
178 | return qreal(mScale); |
179 | } |
180 | |
181 | qreal QWaylandScreen::refreshRate() const |
182 | { |
183 | return mRefreshRate / 1000.f; |
184 | } |
185 | |
186 | #if QT_CONFIG(cursor) |
187 | QPlatformCursor *QWaylandScreen::cursor() const |
188 | { |
189 | return mWaylandDisplay->waylandCursor(); |
190 | } |
191 | #endif // QT_CONFIG(cursor) |
192 | |
193 | QWaylandScreen *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 | |
201 | QWaylandScreen *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 | |
208 | void 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 | |
221 | void 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 | |
239 | void QWaylandScreen::output_scale(int32_t factor) |
240 | { |
241 | mScale = factor; |
242 | } |
243 | |
244 | void 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 | |
257 | void 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 | |
293 | void QWaylandScreen::zxdg_output_v1_logical_position(int32_t x, int32_t y) |
294 | { |
295 | mXdgGeometry.moveTopLeft(p: QPoint(x, y)); |
296 | } |
297 | |
298 | void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height) |
299 | { |
300 | mXdgGeometry.setSize(QSize(width, height)); |
301 | } |
302 | |
303 | void 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 | |
315 | void 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 | |
324 | void 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 | |
332 | QT_END_NAMESPACE |
333 | |