11#include <config-kwin.h>
27#include <KLocalizedString>
29#include <QCoreApplication>
31#include <QSocketNotifier>
32#include <QStringBuilder>
40#include <libdrm/drm_mode.h>
46static QStringList splitPathList(
const QString &
input,
const QChar delimiter)
50 for (
int i = 0; i <
input.size(); i++) {
51 if (
input[i] == delimiter) {
52 if (i > 0 &&
input[i - 1] ==
'\\') {
53 tmp[tmp.size() - 1] = delimiter;
54 }
else if (!tmp.isEmpty()) {
70 , m_udev(std::make_unique<
Udev>())
71 , m_udevMonitor(m_udev->monitor())
73 , m_explicitGpus(splitPathList(qEnvironmentVariable(
"KWIN_DRM_DEVICES"),
':'))
92 if (
const auto gpu =
findGpu(deviceId)) {
93 gpu->setActive(
false);
97 if (
const auto gpu =
findGpu(deviceId)) {
102 if (!m_explicitGpus.isEmpty()) {
103 for (
const QString &fileName : m_explicitGpus) {
107 const auto devices = m_udev->listGPUs();
108 for (
const auto &device : devices) {
109 if (device->seat() == m_session->
seat()) {
110 addGpu(device->devNode());
115 if (m_gpus.empty()) {
116 qCWarning(KWIN_DRM) <<
"No suitable DRM devices have been found";
122 m_udevMonitor->filterSubsystemDevType(
"drm");
123 const int fd = m_udevMonitor->fd();
125 m_socketNotifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Read);
126 connect(m_socketNotifier.get(), &QSocketNotifier::activated,
this, &DrmBackend::handleUdevEvent);
127 m_udevMonitor->enable();
133void DrmBackend::handleUdevEvent()
135 while (
auto device = m_udevMonitor->getDevice()) {
137 if (!m_explicitGpus.isEmpty()) {
138 const auto canonicalPath = QFileInfo(device->devNode()).canonicalPath();
139 const bool foundMatch = std::any_of(m_explicitGpus.begin(), m_explicitGpus.end(), [&canonicalPath](
const QString &explicitPath) {
140 return QFileInfo(explicitPath).canonicalPath() == canonicalPath;
146 if (device->seat() != m_session->
seat()) {
151 if (device->action() == QStringLiteral(
"add")) {
154 qCWarning(KWIN_DRM) <<
"Received unexpected add udev event for:" << device->devNode();
157 if (addGpu(device->devNode())) {
160 }
else if (device->action() == QStringLiteral(
"remove")) {
164 qCCritical(KWIN_DRM) <<
"Primary gpu has been removed! Quitting...";
165 QCoreApplication::exit(1);
172 }
else if (device->action() == QStringLiteral(
"change")) {
175 gpu = addGpu(device->devNode());
177 if (gpu && gpu->isActive()) {
178 qCDebug(KWIN_DRM) <<
"Received change event for monitored drm device" << gpu->devNode();
185DrmGpu *DrmBackend::addGpu(
const QString &fileName)
189 qCWarning(KWIN_DRM) <<
"failed to open drm device at" << fileName;
194 qCDebug(KWIN_DRM) <<
"Skipping KMS incapable drm device node at" << fileName;
200 if (fstat(fd, &buf) == -1) {
201 qCDebug(KWIN_DRM,
"Failed to fstat %s: %s", qPrintable(fileName), strerror(errno));
206 qCDebug(KWIN_DRM,
"adding GPU %s", qPrintable(fileName));
207 m_gpus.push_back(std::make_unique<DrmGpu>(
this, fileName, fd, buf.st_rdev));
208 auto gpu = m_gpus.back().get();
215void DrmBackend::addOutput(DrmAbstractOutput *o)
217 const bool allOff = std::all_of(m_outputs.begin(), m_outputs.end(), [](Output *output) {
218 return output->dpmsMode() != Output::DpmsMode::On;
220 if (allOff && m_recentlyUnpluggedDpmsOffOutputs.contains(o->uuid())) {
221 if (DrmOutput *drmOutput = qobject_cast<DrmOutput *>(o)) {
227 drmOutput->pipeline()->setActive(
false);
228 drmOutput->renderLoop()->inhibit();
229 m_recentlyUnpluggedDpmsOffOutputs.removeOne(drmOutput->uuid());
234 o->updateEnabled(
true);
237void DrmBackend::removeOutput(DrmAbstractOutput *o)
240 const QUuid
id = o->uuid();
241 m_recentlyUnpluggedDpmsOffOutputs.push_back(
id);
242 QTimer::singleShot(1000,
this, [
this,
id]() {
243 m_recentlyUnpluggedDpmsOffOutputs.removeOne(
id);
246 o->updateEnabled(
false);
247 m_outputs.removeOne(o);
253 for (
auto it = m_gpus.begin(); it != m_gpus.end(); ++it) {
254 if ((*it)->isRemoved()) {
255 (*it)->removeOutputs();
257 (*it)->updateOutputs();
263 for (
auto it = m_gpus.begin(); it != m_gpus.end();) {
266 qCDebug(KWIN_DRM) <<
"Removing GPU" << (*it)->
devNode();
267 const std::unique_ptr<DrmGpu> keepAlive = std::move(*it);
268 it = m_gpus.erase(it);
278 return std::make_unique<LibinputBackend>(m_session);
283 return std::make_unique<DrmQPainterBackend>(
this);
288 return std::make_unique<EglGbmBackend>(
this);
293 if (m_outputs.isEmpty()) {
296 for (
const auto &gpu : std::as_const(m_gpus)) {
297 gpu->recreateSurfaces();
310 QDebug s(&supportInfo);
313 <<
"DRM" << Qt::endl;
314 for (
size_t g = 0; g < m_gpus.size(); g++) {
315 s <<
"Atomic Mode Setting on GPU " << g <<
": " << m_gpus.at(g)->atomicModeSetting() << Qt::endl;
329 auto virtualOutput = qobject_cast<DrmVirtualOutput *>(output);
330 if (!virtualOutput) {
339 return m_gpus.empty() ? nullptr : m_gpus.front().get();
344 auto it = std::find_if(m_gpus.begin(), m_gpus.end(), [deviceId](
const auto &gpu) {
345 return gpu->deviceId() == deviceId;
347 return it == m_gpus.end() ? nullptr : it->get();
352 return m_gpus.size();
357 QList<DrmOutput *> toBeEnabled;
358 QList<DrmOutput *> toBeDisabled;
359 for (
const auto &gpu : std::as_const(m_gpus)) {
360 const auto &
outputs = gpu->drmOutputs();
361 for (
const auto &output :
outputs) {
362 if (output->isNonDesktop()) {
366 output->queueChanges(changeset);
367 if (changeset->enabled) {
368 toBeEnabled << output;
370 toBeDisabled << output;
375 for (
const auto &output : std::as_const(toBeEnabled)) {
376 output->revertQueuedChanges();
378 for (
const auto &output : std::as_const(toBeDisabled)) {
379 output->revertQueuedChanges();
386 for (
const auto &output : std::as_const(toBeEnabled)) {
388 output->applyQueuedChanges(changeset);
391 for (
const auto &output : std::as_const(toBeDisabled)) {
393 output->applyQueuedChanges(changeset);
397 for (
const auto &gpu : std::as_const(m_gpus)) {
398 const auto &
outputs = gpu->virtualOutputs();
399 for (
const auto &output :
outputs) {
400 output->applyChanges(config);
408 m_renderBackend = backend;
413 return m_renderBackend;
418 for (
const auto &gpu : std::as_const(m_gpus)) {
419 gpu->releaseBuffers();
430 return m_gpus.front()->eglDisplay();
434#include "moc_drm_backend.cpp"
Outputs outputs() const override
Output * createVirtualOutput(const QString &name, const QSize &size, double scale) override
std::unique_ptr< OpenGLBackend > createOpenGLBackend() override
void setRenderBackend(DrmRenderBackend *backend)
void gpuRemoved(DrmGpu *gpu)
DrmBackend(Session *session, QObject *parent=nullptr)
QList< CompositingType > supportedCompositors() const override
void gpuAdded(DrmGpu *gpu)
bool applyOutputChanges(const OutputConfiguration &config) override
void sceneInitialized() override
Session * session() const override
QString supportInformation() const override
void removeVirtualOutput(Output *output) override
EglDisplay * sceneEglDisplayObject() const override
DrmGpu * primaryGpu() const
std::unique_ptr< QPainterBackend > createQPainterBackend() override
DrmGpu * findGpu(dev_t deviceId) const
std::unique_ptr< InputBackend > createInputBackend() override
DrmRenderBackend * renderBackend() const
const std::vector< std::unique_ptr< DrmGpu > > & gpus() const
bool initialize() override
QList< DrmOutput * > drmOutputs() const
void outputRemoved(DrmAbstractOutput *output)
DrmVirtualOutput * createVirtualOutput(const QString &name, const QSize &size, double scale)
void outputAdded(DrmAbstractOutput *output)
void removeVirtualOutput(DrmVirtualOutput *output)
void outputAdded(Output *output)
void outputRemoved(Output *output)
std::shared_ptr< OutputChangeSet > constChangeSet(Output *output) const
virtual int openRestricted(const QString &fileName)=0
void devicePaused(dev_t deviceId)
void deviceResumed(dev_t deviceId)
virtual QString seat() const =0
virtual void closeRestricted(int fileDescriptor)=0
InputRedirection * input()