KWin
Loading...
Searching...
No Matches
drmlease_v1.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "drmlease_v1.h"
7#include "display.h"
8#include "drmlease_v1_p.h"
9#include "utils/common.h"
10#include "utils/resource.h"
11
12#include <fcntl.h>
13#include <unistd.h>
14
15namespace KWin
16{
17
18static const quint32 s_version = 1;
19
20DrmLeaseManagerV1::DrmLeaseManagerV1(DrmBackend *backend, Display *display, QObject *parent)
21 : QObject(parent)
22 , m_backend(backend)
23 , m_display(display)
24{
25 const auto &gpus = m_backend->gpus();
26 for (const auto &gpu : gpus) {
27 addGpu(gpu.get());
28 }
29 connect(m_backend, &DrmBackend::gpuAdded, this, &DrmLeaseManagerV1::addGpu);
30 connect(m_backend, &DrmBackend::gpuRemoved, this, &DrmLeaseManagerV1::removeGpu);
31 connect(m_backend, &DrmBackend::outputsQueried, this, &DrmLeaseManagerV1::handleOutputsQueried);
32}
33
35{
36 for (const auto device : m_leaseDevices) {
37 device->remove();
38 }
39}
40
41void DrmLeaseManagerV1::addGpu(DrmGpu *gpu)
42{
43 m_leaseDevices[gpu] = new DrmLeaseDeviceV1Interface(m_display, gpu);
44}
45
46void DrmLeaseManagerV1::removeGpu(DrmGpu *gpu)
47{
48 if (auto device = m_leaseDevices.take(gpu)) {
49 device->remove();
50 }
51}
52
53void DrmLeaseManagerV1::handleOutputsQueried()
54{
55 for (const auto device : m_leaseDevices) {
56 device->done();
57 device->setDrmMaster(device->gpu()->isActive());
58 }
59}
60
62 : QtWaylandServer::wp_drm_lease_device_v1(*display, s_version)
63 , m_gpu(gpu)
64{
65 const auto outputs = gpu->drmOutputs();
66 for (const auto output : outputs) {
67 addOutput(output);
68 }
72}
73
75{
76 while (!m_connectors.empty()) {
77 removeOutput(m_connectors.begin()->first);
78 }
79}
80
82{
83 DrmOutput *drmOutput = qobject_cast<DrmOutput *>(output);
84 if (!drmOutput || !drmOutput->isNonDesktop()) {
85 return;
86 }
87 m_connectors[drmOutput] = std::make_unique<DrmLeaseConnectorV1Interface>(this, drmOutput);
88
89 if (m_hasDrmMaster) {
90 offerConnector(m_connectors[drmOutput].get());
91 }
92}
93
95{
96 const auto it = m_connectors.find(output);
97 if (it != m_connectors.end()) {
98 DrmLeaseConnectorV1Interface *connector = it->second.get();
99 connector->withdraw();
100 for (const auto &lease : std::as_const(m_leases)) {
101 if (lease->connectors().contains(connector)) {
102 lease->connectors().removeOne(connector);
103 lease->revoke();
104 }
105 }
106 for (const auto &leaseRequest : std::as_const(m_leaseRequests)) {
107 if (leaseRequest->connectors().contains(connector)) {
108 leaseRequest->invalidate();
109 }
110 }
111 m_connectors.erase(it);
112 }
113}
114
116{
117 if (hasDrmMaster == m_hasDrmMaster) {
118 return;
119 }
120 if (hasDrmMaster) {
121 // send pending drm fds
122 while (!m_pendingFds.isEmpty()) {
123 FileDescriptor fd = m_gpu->createNonMasterFd();
124 send_drm_fd(m_pendingFds.dequeue(), fd.get());
125 }
126 // offer all connectors again
127 for (const auto &[output, connector] : m_connectors) {
128 offerConnector(connector.get());
129 }
130 } else {
131 // withdraw all connectors
132 for (const auto &[output, connector] : m_connectors) {
133 connector->withdraw();
134 }
135 // and revoke all leases
136 for (const auto &lease : std::as_const(m_leases)) {
137 lease->revoke();
138 }
139 }
140 m_hasDrmMaster = hasDrmMaster;
141 done();
142}
143
145{
146 const auto resources = resourceMap();
147 for (const auto resource : resources) {
148 send_done(resource->handle);
149 }
150}
151
153{
154 for (const auto &lease : std::as_const(m_leases)) {
155 lease->deny();
156 }
157 for (const auto &[output, connector] : m_connectors) {
158 connector->withdraw();
159 }
160 for (const auto &request : std::as_const(m_leaseRequests)) {
161 request->invalidate();
162 }
163 done();
164 globalRemove();
165}
166
168{
169 m_leaseRequests.push_back(leaseRequest);
170}
171
173{
174 m_leaseRequests.removeOne(leaseRequest);
175}
176
178{
179 m_leases.push_back(lease);
180}
181
183{
184 m_leases.removeOne(lease);
185}
186
188{
189 return m_hasDrmMaster;
190}
191
193{
194 return m_gpu;
195}
196
198{
199 for (const auto &resource : resourceMap()) {
200 auto connectorResource = connector->add(resource->client(), 0, resource->version());
201 send_connector(resource->handle, connectorResource->handle);
202 connector->send(connectorResource->handle);
203 }
204}
205
206void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_create_lease_request(Resource *resource, uint32_t id)
207{
208 wl_resource *requestResource = wl_resource_create(resource->client(), &wp_drm_lease_request_v1_interface,
209 resource->version(), id);
210 if (!requestResource) {
211 wl_resource_post_no_memory(resource->handle);
212 return;
213 }
214 m_leaseRequests << new DrmLeaseRequestV1Interface(this, requestResource);
215}
216
217void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_release(Resource *resource)
218{
219 send_released(resource->handle);
220 wl_resource_destroy(resource->handle);
221}
222
223void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_bind_resource(Resource *resource)
224{
225 if (isGlobalRemoved()) {
226 return;
227 }
228 if (!m_hasDrmMaster) {
229 m_pendingFds << resource->handle;
230 return;
231 }
232 FileDescriptor fd = m_gpu->createNonMasterFd();
233 send_drm_fd(resource->handle, fd.get());
234 for (const auto &[output, connector] : m_connectors) {
235 if (!connector->withdrawn()) {
236 auto connectorResource = connector->add(resource->client(), 0, s_version);
237 send_connector(resource->handle, connectorResource->handle);
238 connector->send(connectorResource->handle);
239 }
240 }
241 send_done(resource->handle);
242}
243
244void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_destroy_global()
245{
246 delete this;
247}
248
250 : wp_drm_lease_connector_v1()
251 , m_device(leaseDevice)
252 , m_output(output)
253{
254}
255
257{
258 return m_output->connector()->id();
259}
260
265
267{
268 return m_output;
269}
270
272{
273 return m_withdrawn;
274}
275
276void DrmLeaseConnectorV1Interface::send(wl_resource *resource)
277{
278 m_withdrawn = false;
279 send_connector_id(resource, m_output->connector()->id());
280 send_name(resource, m_output->name());
281 send_description(resource, m_output->description());
282 send_done(resource);
283}
284
286{
287 if (!m_withdrawn) {
288 m_withdrawn = true;
289 for (const auto &resource : resourceMap()) {
290 send_withdrawn(resource->handle);
291 }
292 }
293}
294
295void DrmLeaseConnectorV1Interface::wp_drm_lease_connector_v1_destroy(Resource *resource)
296{
297 wl_resource_destroy(resource->handle);
298}
299
301 : wp_drm_lease_request_v1(resource)
302 , m_device(device)
303{
304}
305
310
311QList<DrmLeaseConnectorV1Interface *> DrmLeaseRequestV1Interface::connectors() const
312{
313 return m_connectors;
314}
315
317{
318 m_connectors.clear();
319 m_invalid = true;
320}
321
322void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector_handle)
323{
324 if (auto connector = resource_cast<DrmLeaseConnectorV1Interface *>(connector_handle)) {
325 if (connector->device() != m_device) {
326 wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "Requested connector from invalid lease device");
327 } else if (connector->withdrawn()) {
328 qCWarning(KWIN_CORE) << "DrmLease: withdrawn connector requested";
329 invalidate();
330 } else if (m_connectors.contains(connector)) {
331 wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Requested connector twice");
332 } else if (!m_invalid) {
333 m_connectors << connector;
334 }
335 } else {
336 qCWarning(KWIN_CORE, "DrmLease: Invalid connector requested");
337 invalidate();
338 }
339}
340
342{
343 wl_resource *leaseResource = wl_resource_create(resource->client(), &wp_drm_lease_v1_interface, s_version, id);
344 if (!leaseResource) {
345 wl_resource_post_no_memory(resource->handle);
346 return;
347 }
348 DrmLeaseV1Interface *lease = new DrmLeaseV1Interface(m_device, m_connectors, leaseResource);
349 m_device->addLease(lease);
350 if (!m_device->hasDrmMaster()) {
351 qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request without drm master";
352 lease->deny();
353 } else if (m_invalid) {
354 qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request with a withdrawn connector";
355 lease->deny();
356 } else if (m_connectors.isEmpty()) {
357 wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "Requested lease without connectors");
358 } else {
359 QList<DrmOutput *> outputs;
360 for (const auto &connector : m_connectors) {
361 outputs.push_back(connector->output());
362 }
363 auto drmLease = m_device->gpu()->leaseOutputs(outputs);
364 if (drmLease) {
365 lease->grant(std::move(drmLease));
366 } else {
367 lease->deny();
368 }
369 }
370 wl_resource_destroy(resource->handle);
371}
372
374{
375 delete this;
376}
377
378DrmLeaseV1Interface::DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QList<DrmLeaseConnectorV1Interface *> &connectors, wl_resource *resource)
379 : wp_drm_lease_v1(resource)
380 , m_device(device)
381 , m_connectors(connectors)
382{
383}
384
386{
387 if (m_lease) {
388 revoke();
389 } else {
390 deny();
391 }
392 m_device->removeLease(this);
393}
394
395void DrmLeaseV1Interface::grant(std::unique_ptr<DrmLease> &&lease)
396{
397 FileDescriptor tmp = std::move(lease->fd());
398 send_lease_fd(tmp.get());
399 m_lease = std::move(lease);
400 connect(m_lease.get(), &DrmLease::revokeRequested, this, &DrmLeaseV1Interface::revoke);
401 for (const auto &connector : std::as_const(m_connectors)) {
402 connector->withdraw();
403 }
404 m_device->done();
405}
406
408{
409 Q_ASSERT(!m_lease);
410 if (!m_finished) {
411 m_finished = true;
412 send_finished();
413 }
414}
415
417{
418 Q_ASSERT(m_lease);
419 if (!m_finished) {
420 m_finished = true;
421 send_finished();
422 }
423 m_lease.reset();
424 // check if we should offer connectors again
425 if (m_device->hasDrmMaster()) {
426 for (const auto &connector : std::as_const(m_connectors)) {
427 m_device->offerConnector(connector);
428 }
429 m_device->done();
430 }
431}
432
433void DrmLeaseV1Interface::wp_drm_lease_v1_destroy(Resource *resource)
434{
435 wl_resource_destroy(resource->handle);
436}
437
438void DrmLeaseV1Interface::wp_drm_lease_v1_destroy_resource(Resource *resource)
439{
440 delete this;
441}
442
443QList<DrmLeaseConnectorV1Interface *> DrmLeaseV1Interface::connectors() const
444{
445 return m_connectors;
446}
447
448}
449
450#include "moc_drmlease_v1.cpp"
451#include "moc_drmlease_v1_p.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
void gpuRemoved(DrmGpu *gpu)
void gpuAdded(DrmGpu *gpu)
const std::vector< std::unique_ptr< DrmGpu > > & gpus() const
std::unique_ptr< DrmLease > leaseOutputs(const QList< DrmOutput * > &outputs)
Definition drm_gpu.cpp:611
QList< DrmOutput * > drmOutputs() const
Definition drm_gpu.cpp:643
void outputRemoved(DrmAbstractOutput *output)
void outputAdded(DrmAbstractOutput *output)
FileDescriptor createNonMasterFd() const
Definition drm_gpu.cpp:127
void activeChanged(bool active)
void send(wl_resource *resource)
DrmLeaseDeviceV1Interface * device() const
DrmLeaseConnectorV1Interface(DrmLeaseDeviceV1Interface *leaseDevice, DrmOutput *output)
void setDrmMaster(bool hasDrmMaster)
DrmLeaseDeviceV1Interface(Display *display, DrmGpu *gpu)
void addLease(DrmLeaseV1Interface *lease)
void offerConnector(DrmLeaseConnectorV1Interface *connector)
void addLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
void removeLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
void removeOutput(DrmAbstractOutput *output)
void removeLease(DrmLeaseV1Interface *lease)
void addOutput(DrmAbstractOutput *output)
void revokeRequested()
DrmLeaseManagerV1(DrmBackend *backend, Display *display, QObject *parent=nullptr)
QList< DrmLeaseConnectorV1Interface * > m_connectors
void wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector) override
QList< DrmLeaseConnectorV1Interface * > connectors() const
DrmLeaseRequestV1Interface(DrmLeaseDeviceV1Interface *device, wl_resource *resource)
void wp_drm_lease_request_v1_destroy_resource(Resource *resource) override
void wp_drm_lease_request_v1_submit(Resource *resource, uint32_t id) override
DrmLeaseDeviceV1Interface *const m_device
DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QList< DrmLeaseConnectorV1Interface * > &connectors, wl_resource *resource)
void grant(std::unique_ptr< DrmLease > &&lease)
QList< DrmLeaseConnectorV1Interface * > connectors() const
uint32_t id() const
DrmConnector * connector() const
QString description() const
Definition output.cpp:541
QString name
Definition output.h:136
bool isNonDesktop() const
Definition output.cpp:667