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