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 }