File indexing completed on 2024-11-10 04:56:32

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "virtual_backend.h"
0010 
0011 #include "virtual_egl_backend.h"
0012 #include "virtual_output.h"
0013 #include "virtual_qpainter_backend.h"
0014 
0015 #include <fcntl.h>
0016 #include <gbm.h>
0017 #include <xf86drm.h>
0018 
0019 namespace KWin
0020 {
0021 
0022 static FileDescriptor findRenderDevice()
0023 {
0024     const int deviceCount = drmGetDevices2(0, nullptr, 0);
0025     if (deviceCount <= 0) {
0026         return FileDescriptor{};
0027     }
0028 
0029     QList<drmDevice *> devices(deviceCount);
0030     if (drmGetDevices2(0, devices.data(), devices.size()) < 0) {
0031         return FileDescriptor{};
0032     }
0033     auto deviceCleanup = qScopeGuard([&devices]() {
0034         drmFreeDevices(devices.data(), devices.size());
0035     });
0036 
0037     for (drmDevice *device : std::as_const(devices)) {
0038         // If it's a vgem device, prefer the primary node because gbm will attempt to allocate
0039         // dumb buffers and they can be allocated only on the primary node.
0040         int nodeType = DRM_NODE_RENDER;
0041         if (device->bustype == DRM_BUS_PLATFORM) {
0042             if (strcmp(device->businfo.platform->fullname, "vgem") == 0) {
0043                 nodeType = DRM_NODE_PRIMARY;
0044             }
0045         }
0046 
0047         if (device->available_nodes & (1 << nodeType)) {
0048             FileDescriptor fd{open(device->nodes[nodeType], O_RDWR | O_CLOEXEC)};
0049             if (fd.isValid()) {
0050                 return fd;
0051             }
0052         }
0053     }
0054 
0055     return FileDescriptor{};
0056 }
0057 
0058 VirtualBackend::VirtualBackend(QObject *parent)
0059     : OutputBackend(parent)
0060 {
0061     m_drmFileDescriptor = findRenderDevice();
0062     if (m_drmFileDescriptor.isValid()) {
0063         m_gbmDevice = gbm_create_device(m_drmFileDescriptor.get());
0064     }
0065 }
0066 
0067 VirtualBackend::~VirtualBackend()
0068 {
0069     if (m_gbmDevice) {
0070         gbm_device_destroy(m_gbmDevice);
0071     }
0072 }
0073 
0074 bool VirtualBackend::initialize()
0075 {
0076     return true;
0077 }
0078 
0079 QList<CompositingType> VirtualBackend::supportedCompositors() const
0080 {
0081     QList<CompositingType> compositingTypes;
0082     if (m_gbmDevice) {
0083         compositingTypes.append(OpenGLCompositing);
0084     }
0085     compositingTypes.append(QPainterCompositing);
0086     return compositingTypes;
0087 }
0088 
0089 gbm_device *VirtualBackend::gbmDevice() const
0090 {
0091     return m_gbmDevice;
0092 }
0093 
0094 std::unique_ptr<QPainterBackend> VirtualBackend::createQPainterBackend()
0095 {
0096     return std::make_unique<VirtualQPainterBackend>(this);
0097 }
0098 
0099 std::unique_ptr<OpenGLBackend> VirtualBackend::createOpenGLBackend()
0100 {
0101     return std::make_unique<VirtualEglBackend>(this);
0102 }
0103 
0104 Outputs VirtualBackend::outputs() const
0105 {
0106     return m_outputs;
0107 }
0108 
0109 VirtualOutput *VirtualBackend::createOutput(const OutputInfo &info)
0110 {
0111     VirtualOutput *output = new VirtualOutput(this, info.internal);
0112     output->init(info.geometry.topLeft(), info.geometry.size() * info.scale, info.scale);
0113     m_outputs.append(output);
0114     Q_EMIT outputAdded(output);
0115     output->updateEnabled(true);
0116     return output;
0117 }
0118 
0119 Output *VirtualBackend::addOutput(const OutputInfo &info)
0120 {
0121     VirtualOutput *output = createOutput(info);
0122     Q_EMIT outputsQueried();
0123     return output;
0124 }
0125 
0126 void VirtualBackend::setVirtualOutputs(const QList<OutputInfo> &infos)
0127 {
0128     const QList<VirtualOutput *> removed = m_outputs;
0129 
0130     for (const auto &info : infos) {
0131         createOutput(info);
0132     }
0133 
0134     for (VirtualOutput *output : removed) {
0135         output->updateEnabled(false);
0136         m_outputs.removeOne(output);
0137         Q_EMIT outputRemoved(output);
0138         output->unref();
0139     }
0140 
0141     Q_EMIT outputsQueried();
0142 }
0143 
0144 void VirtualBackend::setEglDisplay(std::unique_ptr<EglDisplay> &&display)
0145 {
0146     m_display = std::move(display);
0147 }
0148 
0149 EglDisplay *VirtualBackend::sceneEglDisplayObject() const
0150 {
0151     return m_display.get();
0152 }
0153 
0154 } // namespace KWin
0155 
0156 #include "moc_virtual_backend.cpp"