1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Pelagicore AG |
5 | ** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> |
6 | ** Contact: https://www.qt.io/licensing/ |
7 | ** |
8 | ** This file is part of the plugins of the Qt Toolkit. |
9 | ** |
10 | ** $QT_BEGIN_LICENSE:LGPL$ |
11 | ** Commercial License Usage |
12 | ** Licensees holding valid commercial Qt licenses may use this file in |
13 | ** accordance with the commercial license agreement provided with the |
14 | ** Software or, alternatively, in accordance with the terms contained in |
15 | ** a written agreement between you and The Qt Company. For licensing terms |
16 | ** and conditions see https://www.qt.io/terms-conditions. For further |
17 | ** information use the contact form at https://www.qt.io/contact-us. |
18 | ** |
19 | ** GNU Lesser General Public License Usage |
20 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
21 | ** General Public License version 3 as published by the Free Software |
22 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
23 | ** packaging of this file. Please review the following information to |
24 | ** ensure the GNU Lesser General Public License version 3 requirements |
25 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
26 | ** |
27 | ** GNU General Public License Usage |
28 | ** Alternatively, this file may be used under the terms of the GNU |
29 | ** General Public License version 2.0 or (at your option) the GNU General |
30 | ** Public license version 3 or any later version approved by the KDE Free |
31 | ** Qt Foundation. The licenses are as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
33 | ** included in the packaging of this file. Please review the following |
34 | ** information to ensure the GNU General Public License requirements will |
35 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
36 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #ifndef QKMSDEVICE_P_H |
43 | #define QKMSDEVICE_P_H |
44 | |
45 | // |
46 | // W A R N I N G |
47 | // ------------- |
48 | // |
49 | // This file is not part of the Qt API. It exists purely as an |
50 | // implementation detail. This header file may change from version to |
51 | // version without notice, or even be removed. |
52 | // |
53 | // We mean it. |
54 | // |
55 | |
56 | #include <QtGui/private/qtguiglobal_p.h> |
57 | #include <qpa/qplatformscreen.h> |
58 | #include <QtCore/QMap> |
59 | #include <QtCore/QVariant> |
60 | #include <QtCore/QThreadStorage> |
61 | |
62 | #include <xf86drm.h> |
63 | #include <xf86drmMode.h> |
64 | #include <drm_fourcc.h> |
65 | |
66 | #include <functional> |
67 | |
68 | // In less fortunate cases one may need to build on a system with dev headers |
69 | // from the dark ages. Let's pull a GL and define the missing stuff outselves. |
70 | |
71 | #ifndef DRM_PLANE_TYPE_OVERLAY |
72 | #define DRM_PLANE_TYPE_OVERLAY 0 |
73 | #endif |
74 | #ifndef DRM_PLANE_TYPE_PRIMARY |
75 | #define DRM_PLANE_TYPE_PRIMARY 1 |
76 | #endif |
77 | #ifndef DRM_PLANE_TYPE_CURSOR |
78 | #define DRM_PLANE_TYPE_CURSOR 2 |
79 | #endif |
80 | |
81 | #ifndef DRM_CLIENT_CAP_UNIVERSAL_PLANES |
82 | #define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 |
83 | #endif |
84 | #ifndef DRM_CLIENT_CAP_ATOMIC |
85 | #define DRM_CLIENT_CAP_ATOMIC 3 |
86 | #endif |
87 | |
88 | #ifndef DRM_MODE_PROP_EXTENDED_TYPE |
89 | #define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 |
90 | #endif |
91 | #ifndef DRM_MODE_PROP_TYPE |
92 | #define DRM_MODE_PROP_TYPE(n) ((n) << 6) |
93 | #endif |
94 | #ifndef DRM_MODE_PROP_OBJECT |
95 | #define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) |
96 | #endif |
97 | #ifndef DRM_MODE_PROP_SIGNED_RANGE |
98 | #define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) |
99 | #endif |
100 | |
101 | QT_BEGIN_NAMESPACE |
102 | |
103 | class QKmsDevice; |
104 | |
105 | class QKmsScreenConfig |
106 | { |
107 | public: |
108 | enum VirtualDesktopLayout { |
109 | VirtualDesktopLayoutHorizontal, |
110 | VirtualDesktopLayoutVertical |
111 | }; |
112 | |
113 | QKmsScreenConfig(); |
114 | |
115 | QString devicePath() const { return m_devicePath; } |
116 | |
117 | bool headless() const { return m_headless; } |
118 | QSize headlessSize() const { return m_headlessSize; } |
119 | bool hwCursor() const { return m_hwCursor; } |
120 | bool separateScreens() const { return m_separateScreens; } |
121 | bool supportsPBuffers() const { return m_pbuffers; } |
122 | VirtualDesktopLayout virtualDesktopLayout() const { return m_virtualDesktopLayout; } |
123 | |
124 | QMap<QString, QVariantMap> outputSettings() const { return m_outputSettings; } |
125 | |
126 | private: |
127 | void loadConfig(); |
128 | |
129 | QString m_devicePath; |
130 | bool m_headless; |
131 | QSize m_headlessSize; |
132 | bool m_hwCursor; |
133 | bool m_separateScreens; |
134 | bool m_pbuffers; |
135 | VirtualDesktopLayout m_virtualDesktopLayout; |
136 | QMap<QString, QVariantMap> m_outputSettings; |
137 | }; |
138 | |
139 | // NB! QKmsPlane does not store the current state and offers no functions to |
140 | // change object properties. Any such functionality belongs to subclasses since |
141 | // in some cases atomic operations will be desired where a mere |
142 | // drmModeObjectSetProperty would not be acceptable. |
143 | struct QKmsPlane |
144 | { |
145 | enum Type { |
146 | OverlayPlane = DRM_PLANE_TYPE_OVERLAY, |
147 | PrimaryPlane = DRM_PLANE_TYPE_PRIMARY, |
148 | CursorPlane = DRM_PLANE_TYPE_CURSOR |
149 | }; |
150 | |
151 | enum Rotation { |
152 | Rotation0 = 1 << 0, |
153 | Rotation90 = 1 << 1, |
154 | Rotation180 = 1 << 2, |
155 | Rotation270 = 1 << 3, |
156 | RotationReflectX = 1 << 4, |
157 | RotationReflectY = 1 << 5 |
158 | }; |
159 | Q_DECLARE_FLAGS(Rotations, Rotation) |
160 | |
161 | uint32_t id = 0; |
162 | Type type = OverlayPlane; |
163 | |
164 | int possibleCrtcs = 0; |
165 | |
166 | QVector<uint32_t> supportedFormats; |
167 | |
168 | Rotations initialRotation = Rotation0; |
169 | Rotations availableRotations = Rotation0; |
170 | uint32_t rotationPropertyId = 0; |
171 | uint32_t crtcPropertyId = 0; |
172 | uint32_t framebufferPropertyId = 0; |
173 | uint32_t srcXPropertyId = 0; |
174 | uint32_t srcYPropertyId = 0; |
175 | uint32_t crtcXPropertyId = 0; |
176 | uint32_t crtcYPropertyId = 0; |
177 | uint32_t srcwidthPropertyId = 0; |
178 | uint32_t srcheightPropertyId = 0; |
179 | uint32_t crtcwidthPropertyId = 0; |
180 | uint32_t crtcheightPropertyId = 0; |
181 | uint32_t zposPropertyId = 0; |
182 | uint32_t blendOpPropertyId = 0; |
183 | |
184 | uint32_t activeCrtcId = 0; |
185 | }; |
186 | |
187 | Q_DECLARE_OPERATORS_FOR_FLAGS(QKmsPlane::Rotations) |
188 | |
189 | struct QKmsOutput |
190 | { |
191 | QString name; |
192 | uint32_t connector_id = 0; |
193 | uint32_t crtc_index = 0; |
194 | uint32_t crtc_id = 0; |
195 | QSizeF physical_size; |
196 | int preferred_mode = -1; // index of preferred mode in list below |
197 | int mode = -1; // index of selected mode in list below |
198 | bool mode_set = false; |
199 | drmModeCrtcPtr saved_crtc = nullptr; |
200 | QList<drmModeModeInfo> modes; |
201 | int subpixel = DRM_MODE_SUBPIXEL_UNKNOWN; |
202 | drmModePropertyPtr dpms_prop = nullptr; |
203 | drmModePropertyBlobPtr edid_blob = nullptr; |
204 | bool wants_forced_plane = false; |
205 | uint32_t forced_plane_id = 0; |
206 | bool forced_plane_set = false; |
207 | uint32_t drm_format = DRM_FORMAT_XRGB8888; |
208 | bool drm_format_requested_by_user = false; |
209 | QString clone_source; |
210 | QVector<QKmsPlane> available_planes; |
211 | struct QKmsPlane *eglfs_plane = nullptr; |
212 | QSize size; |
213 | uint32_t crtcIdPropertyId = 0; |
214 | uint32_t modeIdPropertyId = 0; |
215 | uint32_t activePropertyId = 0; |
216 | |
217 | uint32_t mode_blob_id = 0; |
218 | |
219 | void restoreMode(QKmsDevice *device); |
220 | void cleanup(QKmsDevice *device); |
221 | QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const; |
222 | void setPowerState(QKmsDevice *device, QPlatformScreen::PowerState state); |
223 | }; |
224 | |
225 | class QKmsDevice |
226 | { |
227 | public: |
228 | struct ScreenInfo { |
229 | int virtualIndex = 0; |
230 | QPoint virtualPos; |
231 | bool isPrimary = false; |
232 | QKmsOutput output; |
233 | }; |
234 | |
235 | QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path = QString()); |
236 | virtual ~QKmsDevice(); |
237 | |
238 | virtual bool open() = 0; |
239 | virtual void close() = 0; |
240 | virtual void *nativeDisplay() const = 0; |
241 | |
242 | bool hasAtomicSupport(); |
243 | |
244 | #if QT_CONFIG(drm_atomic) |
245 | drmModeAtomicReq *threadLocalAtomicRequest(); |
246 | bool threadLocalAtomicCommit(void *user_data); |
247 | void threadLocalAtomicReset(); |
248 | #endif |
249 | void createScreens(); |
250 | |
251 | int fd() const; |
252 | QString devicePath() const; |
253 | |
254 | QKmsScreenConfig *screenConfig() const; |
255 | |
256 | protected: |
257 | virtual QPlatformScreen *createScreen(const QKmsOutput &output) = 0; |
258 | virtual QPlatformScreen *createHeadlessScreen(); |
259 | virtual void registerScreenCloning(QPlatformScreen *screen, |
260 | QPlatformScreen *screenThisScreenClones, |
261 | const QVector<QPlatformScreen *> &screensCloningThisScreen); |
262 | virtual void registerScreen(QPlatformScreen *screen, |
263 | bool isPrimary, |
264 | const QPoint &virtualPos, |
265 | const QList<QPlatformScreen *> &virtualSiblings) = 0; |
266 | |
267 | void setFd(int fd); |
268 | int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); |
269 | QPlatformScreen *createScreenForConnector(drmModeResPtr resources, |
270 | drmModeConnectorPtr connector, |
271 | ScreenInfo *vinfo); |
272 | drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); |
273 | drmModePropertyBlobPtr connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name); |
274 | typedef std::function<void(drmModePropertyPtr, quint64)> PropCallback; |
275 | void enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback); |
276 | void discoverPlanes(); |
277 | void parseConnectorProperties(uint32_t connectorId, QKmsOutput *output); |
278 | void parseCrtcProperties(uint32_t crtcId, QKmsOutput *output); |
279 | |
280 | QKmsScreenConfig *m_screenConfig; |
281 | QString m_path; |
282 | int m_dri_fd; |
283 | |
284 | bool m_has_atomic_support; |
285 | |
286 | #if QT_CONFIG(drm_atomic) |
287 | struct AtomicReqs { |
288 | drmModeAtomicReq *request = nullptr; |
289 | drmModeAtomicReq *previous_request = nullptr; |
290 | }; |
291 | QThreadStorage<AtomicReqs> m_atomicReqs; |
292 | #endif |
293 | quint32 m_crtc_allocator; |
294 | |
295 | QVector<QKmsPlane> m_planes; |
296 | |
297 | private: |
298 | Q_DISABLE_COPY(QKmsDevice) |
299 | }; |
300 | |
301 | QT_END_NAMESPACE |
302 | |
303 | #endif |
304 | |