File indexing completed on 2024-05-19 16:35:18

0001 /*
0002     SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "drmlease_v1_interface.h"
0007 #include "display.h"
0008 #include "drmlease_v1_interface_p.h"
0009 #include "utils.h"
0010 #include "utils/common.h"
0011 
0012 #include <fcntl.h>
0013 #include <unistd.h>
0014 
0015 namespace KWaylandServer
0016 {
0017 
0018 static const quint32 s_version = 1;
0019 
0020 DrmLeaseManagerV1::DrmLeaseManagerV1(KWin::DrmBackend *backend, Display *display, QObject *parent)
0021     : QObject(parent)
0022     , m_backend(backend)
0023     , m_display(display)
0024 {
0025     const auto &gpus = m_backend->gpus();
0026     for (const auto &gpu : gpus) {
0027         addGpu(gpu.get());
0028     }
0029     connect(m_backend, &KWin::DrmBackend::gpuAdded, this, &DrmLeaseManagerV1::addGpu);
0030     connect(m_backend, &KWin::DrmBackend::gpuRemoved, this, &DrmLeaseManagerV1::removeGpu);
0031     connect(m_backend, &KWin::DrmBackend::outputsQueried, this, &DrmLeaseManagerV1::handleOutputsQueried);
0032     connect(m_backend, &KWin::DrmBackend::activeChanged, this, [this]() {
0033         if (!m_backend->isActive()) {
0034             for (const auto device : m_leaseDevices) {
0035                 device->setDrmMaster(false);
0036             }
0037         }
0038     });
0039 }
0040 
0041 DrmLeaseManagerV1::~DrmLeaseManagerV1()
0042 {
0043     for (const auto device : m_leaseDevices) {
0044         device->remove();
0045     }
0046 }
0047 
0048 void DrmLeaseManagerV1::addGpu(KWin::DrmGpu *gpu)
0049 {
0050     m_leaseDevices[gpu] = new DrmLeaseDeviceV1Interface(m_display, gpu);
0051 }
0052 
0053 void DrmLeaseManagerV1::removeGpu(KWin::DrmGpu *gpu)
0054 {
0055     if (auto device = m_leaseDevices.take(gpu)) {
0056         device->remove();
0057     }
0058 }
0059 
0060 void DrmLeaseManagerV1::handleOutputsQueried()
0061 {
0062     for (const auto device : m_leaseDevices) {
0063         device->done();
0064         device->setDrmMaster(m_backend->isActive());
0065     }
0066 }
0067 
0068 DrmLeaseDeviceV1Interface::DrmLeaseDeviceV1Interface(Display *display, KWin::DrmGpu *gpu)
0069     : QtWaylandServer::wp_drm_lease_device_v1(*display, s_version)
0070     , m_gpu(gpu)
0071 {
0072     const auto outputs = gpu->drmOutputs();
0073     for (const auto output : outputs) {
0074         addOutput(output);
0075     }
0076     connect(gpu, &KWin::DrmGpu::outputAdded, this, &DrmLeaseDeviceV1Interface::addOutput);
0077     connect(gpu, &KWin::DrmGpu::outputRemoved, this, &DrmLeaseDeviceV1Interface::removeOutput);
0078 }
0079 
0080 DrmLeaseDeviceV1Interface::~DrmLeaseDeviceV1Interface()
0081 {
0082     while (!m_connectors.empty()) {
0083         removeOutput(m_connectors.begin()->first);
0084     }
0085 }
0086 
0087 void DrmLeaseDeviceV1Interface::addOutput(KWin::DrmAbstractOutput *output)
0088 {
0089     KWin::DrmOutput *drmOutput = qobject_cast<KWin::DrmOutput *>(output);
0090     if (!drmOutput || !drmOutput->isNonDesktop()) {
0091         return;
0092     }
0093     m_connectors[drmOutput] = std::make_unique<DrmLeaseConnectorV1Interface>(this, drmOutput);
0094 
0095     if (m_hasDrmMaster) {
0096         offerConnector(m_connectors[drmOutput].get());
0097     }
0098 }
0099 
0100 void DrmLeaseDeviceV1Interface::removeOutput(KWin::DrmAbstractOutput *output)
0101 {
0102     const auto it = m_connectors.find(output);
0103     if (it != m_connectors.end()) {
0104         DrmLeaseConnectorV1Interface *connector = it->second.get();
0105         connector->withdraw();
0106         for (const auto &lease : std::as_const(m_leases)) {
0107             if (lease->connectors().contains(connector)) {
0108                 lease->connectors().removeOne(connector);
0109                 lease->revoke();
0110             }
0111         }
0112         for (const auto &leaseRequest : std::as_const(m_leaseRequests)) {
0113             if (leaseRequest->connectors().contains(connector)) {
0114                 leaseRequest->invalidate();
0115             }
0116         }
0117         m_connectors.erase(it);
0118     }
0119 }
0120 
0121 void DrmLeaseDeviceV1Interface::setDrmMaster(bool hasDrmMaster)
0122 {
0123     if (hasDrmMaster == m_hasDrmMaster) {
0124         return;
0125     }
0126     if (hasDrmMaster) {
0127         // send pending drm fds
0128         while (!m_pendingFds.isEmpty()) {
0129             KWin::FileDescriptor fd = m_gpu->createNonMasterFd();
0130             send_drm_fd(m_pendingFds.dequeue(), fd.get());
0131         }
0132         // offer all connectors again
0133         for (const auto &[output, connector] : m_connectors) {
0134             offerConnector(connector.get());
0135         }
0136     } else {
0137         // withdraw all connectors
0138         for (const auto &[output, connector] : m_connectors) {
0139             connector->withdraw();
0140         }
0141         // and revoke all leases
0142         for (const auto &lease : std::as_const(m_leases)) {
0143             lease->revoke();
0144         }
0145     }
0146     m_hasDrmMaster = hasDrmMaster;
0147     done();
0148 }
0149 
0150 void DrmLeaseDeviceV1Interface::done()
0151 {
0152     const auto resources = resourceMap();
0153     for (const auto resource : resources) {
0154         send_done(resource->handle);
0155     }
0156 }
0157 
0158 void DrmLeaseDeviceV1Interface::remove()
0159 {
0160     for (const auto &lease : std::as_const(m_leases)) {
0161         lease->deny();
0162     }
0163     for (const auto &[output, connector] : m_connectors) {
0164         connector->withdraw();
0165     }
0166     for (const auto &request : std::as_const(m_leaseRequests)) {
0167         request->invalidate();
0168     }
0169     done();
0170     globalRemove();
0171 }
0172 
0173 void DrmLeaseDeviceV1Interface::addLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
0174 {
0175     m_leaseRequests.push_back(leaseRequest);
0176 }
0177 
0178 void DrmLeaseDeviceV1Interface::removeLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
0179 {
0180     m_leaseRequests.removeOne(leaseRequest);
0181 }
0182 
0183 void DrmLeaseDeviceV1Interface::addLease(DrmLeaseV1Interface *lease)
0184 {
0185     m_leases.push_back(lease);
0186 }
0187 
0188 void DrmLeaseDeviceV1Interface::removeLease(DrmLeaseV1Interface *lease)
0189 {
0190     m_leases.removeOne(lease);
0191 }
0192 
0193 bool DrmLeaseDeviceV1Interface::hasDrmMaster() const
0194 {
0195     return m_hasDrmMaster;
0196 }
0197 
0198 KWin::DrmGpu *DrmLeaseDeviceV1Interface::gpu() const
0199 {
0200     return m_gpu;
0201 }
0202 
0203 void DrmLeaseDeviceV1Interface::offerConnector(DrmLeaseConnectorV1Interface *connector)
0204 {
0205     for (const auto &resource : resourceMap()) {
0206         auto connectorResource = connector->add(resource->client(), 0, resource->version());
0207         send_connector(resource->handle, connectorResource->handle);
0208         connector->send(connectorResource->handle);
0209     }
0210 }
0211 
0212 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_create_lease_request(Resource *resource, uint32_t id)
0213 {
0214     wl_resource *requestResource = wl_resource_create(resource->client(), &wp_drm_lease_request_v1_interface,
0215                                                       resource->version(), id);
0216     if (!requestResource) {
0217         wl_resource_post_no_memory(resource->handle);
0218         return;
0219     }
0220     m_leaseRequests << new DrmLeaseRequestV1Interface(this, requestResource);
0221 }
0222 
0223 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_release(Resource *resource)
0224 {
0225     send_released(resource->handle);
0226     wl_resource_destroy(resource->handle);
0227 }
0228 
0229 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_bind_resource(Resource *resource)
0230 {
0231     if (isGlobalRemoved()) {
0232         return;
0233     }
0234     if (!m_hasDrmMaster) {
0235         m_pendingFds << resource->handle;
0236         return;
0237     }
0238     KWin::FileDescriptor fd = m_gpu->createNonMasterFd();
0239     send_drm_fd(resource->handle, fd.get());
0240     for (const auto &[output, connector] : m_connectors) {
0241         if (!connector->withdrawn()) {
0242             auto connectorResource = connector->add(resource->client(), 0, s_version);
0243             send_connector(resource->handle, connectorResource->handle);
0244             connector->send(connectorResource->handle);
0245         }
0246     }
0247     send_done(resource->handle);
0248 }
0249 
0250 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_destroy_global()
0251 {
0252     delete this;
0253 }
0254 
0255 DrmLeaseConnectorV1Interface::DrmLeaseConnectorV1Interface(DrmLeaseDeviceV1Interface *leaseDevice, KWin::DrmOutput *output)
0256     : wp_drm_lease_connector_v1()
0257     , m_device(leaseDevice)
0258     , m_output(output)
0259 {
0260 }
0261 
0262 uint32_t DrmLeaseConnectorV1Interface::id() const
0263 {
0264     return m_output->connector()->id();
0265 }
0266 
0267 DrmLeaseDeviceV1Interface *DrmLeaseConnectorV1Interface::device() const
0268 {
0269     return m_device;
0270 }
0271 
0272 KWin::DrmOutput *DrmLeaseConnectorV1Interface::output() const
0273 {
0274     return m_output;
0275 }
0276 
0277 bool DrmLeaseConnectorV1Interface::withdrawn() const
0278 {
0279     return m_withdrawn;
0280 }
0281 
0282 void DrmLeaseConnectorV1Interface::send(wl_resource *resource)
0283 {
0284     m_withdrawn = false;
0285     send_connector_id(resource, m_output->connector()->id());
0286     send_name(resource, m_output->name());
0287     send_description(resource, m_output->description());
0288     send_done(resource);
0289 }
0290 
0291 void DrmLeaseConnectorV1Interface::withdraw()
0292 {
0293     if (!m_withdrawn) {
0294         m_withdrawn = true;
0295         for (const auto &resource : resourceMap()) {
0296             send_withdrawn(resource->handle);
0297         }
0298     }
0299 }
0300 
0301 void DrmLeaseConnectorV1Interface::wp_drm_lease_connector_v1_destroy(Resource *resource)
0302 {
0303     wl_resource_destroy(resource->handle);
0304 }
0305 
0306 DrmLeaseRequestV1Interface::DrmLeaseRequestV1Interface(DrmLeaseDeviceV1Interface *device, wl_resource *resource)
0307     : wp_drm_lease_request_v1(resource)
0308     , m_device(device)
0309 {
0310 }
0311 
0312 DrmLeaseRequestV1Interface::~DrmLeaseRequestV1Interface()
0313 {
0314     m_device->removeLeaseRequest(this);
0315 }
0316 
0317 QVector<DrmLeaseConnectorV1Interface *> DrmLeaseRequestV1Interface::connectors() const
0318 {
0319     return m_connectors;
0320 }
0321 
0322 void DrmLeaseRequestV1Interface::invalidate()
0323 {
0324     m_connectors.clear();
0325     m_invalid = true;
0326 }
0327 
0328 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector_handle)
0329 {
0330     if (auto connector = resource_cast<DrmLeaseConnectorV1Interface *>(connector_handle)) {
0331         if (connector->device() != m_device) {
0332             wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "Requested connector from invalid lease device");
0333         } else if (connector->withdrawn()) {
0334             qCWarning(KWIN_CORE) << "DrmLease: withdrawn connector requested";
0335             invalidate();
0336         } else if (m_connectors.contains(connector)) {
0337             wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Requested connector twice");
0338         } else if (!m_invalid) {
0339             m_connectors << connector;
0340         }
0341     } else {
0342         qCWarning(KWIN_CORE, "DrmLease: Invalid connector requested");
0343         invalidate();
0344     }
0345 }
0346 
0347 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_submit(Resource *resource, uint32_t id)
0348 {
0349     wl_resource *leaseResource = wl_resource_create(resource->client(), &wp_drm_lease_v1_interface, s_version, id);
0350     if (!leaseResource) {
0351         wl_resource_post_no_memory(resource->handle);
0352         return;
0353     }
0354     DrmLeaseV1Interface *lease = new DrmLeaseV1Interface(m_device, m_connectors, leaseResource);
0355     m_device->addLease(lease);
0356     if (!m_device->hasDrmMaster()) {
0357         qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request without drm master";
0358         lease->deny();
0359     } else if (m_invalid) {
0360         qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request with a withdrawn connector";
0361         lease->deny();
0362     } else if (m_connectors.isEmpty()) {
0363         wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "Requested lease without connectors");
0364     } else {
0365         QVector<KWin::DrmOutput *> outputs;
0366         for (const auto &connector : m_connectors) {
0367             outputs.push_back(connector->output());
0368         }
0369         auto drmLease = m_device->gpu()->leaseOutputs(outputs);
0370         if (drmLease) {
0371             lease->grant(std::move(drmLease));
0372         } else {
0373             lease->deny();
0374         }
0375     }
0376     wl_resource_destroy(resource->handle);
0377 }
0378 
0379 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_destroy_resource(Resource *resource)
0380 {
0381     delete this;
0382 }
0383 
0384 DrmLeaseV1Interface::DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QVector<DrmLeaseConnectorV1Interface *> &connectors, wl_resource *resource)
0385     : wp_drm_lease_v1(resource)
0386     , m_device(device)
0387     , m_connectors(connectors)
0388 {
0389 }
0390 
0391 DrmLeaseV1Interface::~DrmLeaseV1Interface()
0392 {
0393     if (m_lease) {
0394         revoke();
0395     } else {
0396         deny();
0397     }
0398     m_device->removeLease(this);
0399 }
0400 
0401 void DrmLeaseV1Interface::grant(std::unique_ptr<KWin::DrmLease> &&lease)
0402 {
0403     KWin::FileDescriptor tmp = std::move(lease->fd());
0404     send_lease_fd(tmp.get());
0405     m_lease = std::move(lease);
0406     connect(m_lease.get(), &KWin::DrmLease::revokeRequested, this, &DrmLeaseV1Interface::revoke);
0407     for (const auto &connector : std::as_const(m_connectors)) {
0408         connector->withdraw();
0409     }
0410     m_device->done();
0411 }
0412 
0413 void DrmLeaseV1Interface::deny()
0414 {
0415     Q_ASSERT(!m_lease);
0416     if (!m_finished) {
0417         m_finished = true;
0418         send_finished();
0419     }
0420 }
0421 
0422 void DrmLeaseV1Interface::revoke()
0423 {
0424     Q_ASSERT(m_lease);
0425     if (!m_finished) {
0426         m_finished = true;
0427         send_finished();
0428     }
0429     m_lease.reset();
0430     // check if we should offer connectors again
0431     if (m_device->hasDrmMaster()) {
0432         for (const auto &connector : std::as_const(m_connectors)) {
0433             m_device->offerConnector(connector);
0434         }
0435         m_device->done();
0436     }
0437 }
0438 
0439 void DrmLeaseV1Interface::wp_drm_lease_v1_destroy(Resource *resource)
0440 {
0441     wl_resource_destroy(resource->handle);
0442 }
0443 
0444 void DrmLeaseV1Interface::wp_drm_lease_v1_destroy_resource(Resource *resource)
0445 {
0446     delete this;
0447 }
0448 
0449 QVector<DrmLeaseConnectorV1Interface *> DrmLeaseV1Interface::connectors() const
0450 {
0451     return m_connectors;
0452 }
0453 
0454 }