File indexing completed on 2024-04-28 05:30:13

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "compositor_wayland.h"
0011 #include "core/output.h"
0012 #include "core/outputbackend.h"
0013 #include "core/renderbackend.h"
0014 #include "core/renderlayer.h"
0015 #include "effect/effecthandler.h"
0016 #include "main.h"
0017 #include "opengl/glplatform.h"
0018 #include "platformsupport/scenes/opengl/openglbackend.h"
0019 #include "platformsupport/scenes/qpainter/qpainterbackend.h"
0020 #include "scene/cursordelegate_opengl.h"
0021 #include "scene/cursordelegate_qpainter.h"
0022 #include "scene/cursorscene.h"
0023 #include "scene/itemrenderer_opengl.h"
0024 #include "scene/itemrenderer_qpainter.h"
0025 #include "scene/workspacescene_opengl.h"
0026 #include "scene/workspacescene_qpainter.h"
0027 #include "window.h"
0028 #include "workspace.h"
0029 
0030 #include <QQuickWindow>
0031 
0032 namespace KWin
0033 {
0034 
0035 WaylandCompositor *WaylandCompositor::create(QObject *parent)
0036 {
0037     Q_ASSERT(!s_compositor);
0038     auto *compositor = new WaylandCompositor(parent);
0039     s_compositor = compositor;
0040     return compositor;
0041 }
0042 
0043 WaylandCompositor::WaylandCompositor(QObject *parent)
0044     : Compositor(parent)
0045 {
0046 }
0047 
0048 WaylandCompositor::~WaylandCompositor()
0049 {
0050     Q_EMIT aboutToDestroy();
0051     stop(); // this can't be called in the destructor of Compositor
0052 }
0053 
0054 bool WaylandCompositor::attemptOpenGLCompositing()
0055 {
0056     std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
0057     if (!backend) {
0058         return false;
0059     }
0060     if (!backend->isFailed()) {
0061         backend->init();
0062     }
0063     if (backend->isFailed()) {
0064         return false;
0065     }
0066 
0067     const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
0068     if (!forceEnv.isEmpty()) {
0069         if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
0070             qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
0071         } else {
0072             // OpenGL 2 disabled by environment variable
0073             return false;
0074         }
0075     } else {
0076         if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
0077             qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
0078             return false;
0079         }
0080     }
0081 
0082     // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
0083     if (!hasGLVersion(2, 0)) {
0084         qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
0085         return false;
0086     }
0087 
0088     m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
0089     m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
0090     m_backend = std::move(backend);
0091 
0092     qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
0093     return true;
0094 }
0095 
0096 bool WaylandCompositor::attemptQPainterCompositing()
0097 {
0098     std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend());
0099     if (!backend || backend->isFailed()) {
0100         return false;
0101     }
0102 
0103     m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get());
0104     m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>());
0105     m_backend = std::move(backend);
0106 
0107     qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized";
0108     return true;
0109 }
0110 
0111 void WaylandCompositor::start()
0112 {
0113     if (kwinApp()->isTerminating()) {
0114         return;
0115     }
0116     if (m_state != State::Off) {
0117         return;
0118     }
0119 
0120     Q_EMIT aboutToToggleCompositing();
0121     m_state = State::Starting;
0122 
0123     // If compositing has been restarted, try to use the last used compositing type.
0124     const QList<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors();
0125     QList<CompositingType> candidateCompositors;
0126 
0127     if (m_selectedCompositor != NoCompositing) {
0128         candidateCompositors.append(m_selectedCompositor);
0129     } else {
0130         candidateCompositors = availableCompositors;
0131 
0132         const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
0133         if (userConfigIt != candidateCompositors.end()) {
0134             candidateCompositors.erase(userConfigIt);
0135             candidateCompositors.prepend(options->compositingMode());
0136         } else {
0137             qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
0138         }
0139     }
0140 
0141     for (auto type : std::as_const(candidateCompositors)) {
0142         bool stop = false;
0143         switch (type) {
0144         case OpenGLCompositing:
0145             qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
0146             stop = attemptOpenGLCompositing();
0147             break;
0148         case QPainterCompositing:
0149             qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
0150             stop = attemptQPainterCompositing();
0151             break;
0152         case NoCompositing:
0153             qCDebug(KWIN_CORE) << "Starting without compositing...";
0154             stop = true;
0155             break;
0156         }
0157 
0158         if (stop) {
0159             break;
0160         } else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) {
0161             qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting.";
0162             qApp->quit();
0163         }
0164     }
0165 
0166     if (!m_backend) {
0167         m_state = State::Off;
0168 
0169         qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
0170         qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
0171         qApp->quit();
0172         return;
0173     }
0174 
0175     if (m_selectedCompositor == NoCompositing) {
0176         m_selectedCompositor = m_backend->compositingType();
0177 
0178         switch (m_selectedCompositor) {
0179         case NoCompositing:
0180             break;
0181         case OpenGLCompositing:
0182             QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
0183             break;
0184         case QPainterCompositing:
0185             QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
0186             break;
0187         }
0188     }
0189 
0190     Q_EMIT sceneCreated();
0191 
0192     const QList<Output *> outputs = workspace()->outputs();
0193     for (Output *output : outputs) {
0194         addOutput(output);
0195     }
0196     connect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput);
0197     connect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput);
0198 
0199     m_state = State::On;
0200 
0201     const auto windows = workspace()->windows();
0202     for (Window *window : windows) {
0203         window->setupCompositing();
0204     }
0205 
0206     // Sets also the 'effects' pointer.
0207     kwinApp()->createEffectsHandler(this, m_scene.get());
0208 
0209     Q_EMIT compositingToggled(true);
0210 }
0211 
0212 void WaylandCompositor::stop()
0213 {
0214     if (m_state == State::Off || m_state == State::Stopping) {
0215         return;
0216     }
0217     m_state = State::Stopping;
0218     Q_EMIT aboutToToggleCompositing();
0219 
0220     // Some effects might need access to effect windows when they are about to
0221     // be destroyed, for example to unreference deleted windows, so we have to
0222     // make sure that effect windows outlive effects.
0223     delete effects;
0224     effects = nullptr;
0225 
0226     if (Workspace::self()) {
0227         const auto windows = workspace()->windows();
0228         for (Window *window : windows) {
0229             window->finishCompositing();
0230         }
0231         disconnect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput);
0232         disconnect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput);
0233     }
0234 
0235     if (m_backend->compositingType() == OpenGLCompositing) {
0236         // some layers need a context current for destruction
0237         static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent();
0238     }
0239 
0240     const auto superlayers = m_superlayers;
0241     for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
0242         removeSuperLayer(*it);
0243     }
0244 
0245     m_scene.reset();
0246     m_cursorScene.reset();
0247     m_backend.reset();
0248 
0249     m_state = State::Off;
0250     Q_EMIT compositingToggled(false);
0251 }
0252 
0253 void WaylandCompositor::addOutput(Output *output)
0254 {
0255     if (output->isPlaceholder()) {
0256         return;
0257     }
0258     auto workspaceLayer = new RenderLayer(output->renderLoop());
0259     workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output));
0260     workspaceLayer->setGeometry(output->rectF());
0261     connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() {
0262         workspaceLayer->setGeometry(output->rectF());
0263     });
0264 
0265     auto cursorLayer = new RenderLayer(output->renderLoop());
0266     cursorLayer->setVisible(false);
0267     if (m_backend->compositingType() == OpenGLCompositing) {
0268         cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>(output));
0269     } else {
0270         cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>(output));
0271     }
0272     cursorLayer->setParent(workspaceLayer);
0273     cursorLayer->setSuperlayer(workspaceLayer);
0274 
0275     // Software cursor is forced for intel devices because there are screen stuttering issues with hardware cursor,
0276     // possibly a kernel driver bug. Remove the workaround when https://gitlab.freedesktop.org/drm/intel/-/issues/9571 is fixed.
0277     static bool forceSoftwareCursorIsSet;
0278     static const bool forceSoftwareCursor = qEnvironmentVariableIntValue("KWIN_FORCE_SW_CURSOR", &forceSoftwareCursorIsSet) == 1
0279         || (!forceSoftwareCursorIsSet && GLPlatform::instance() && GLPlatform::instance()->isIntel());
0280 
0281     auto updateCursorLayer = [this, output, cursorLayer]() {
0282         const Cursor *cursor = Cursors::self()->currentCursor();
0283         const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
0284         const auto outputLayer = m_backend->cursorLayer(output);
0285         if (!cursor->isOnOutput(output)) {
0286             if (outputLayer && outputLayer->isEnabled()) {
0287                 outputLayer->setEnabled(false);
0288                 output->updateCursorLayer();
0289             }
0290             cursorLayer->setVisible(false);
0291             return true;
0292         }
0293         const auto renderHardwareCursor = [&]() {
0294             if (!outputLayer || forceSoftwareCursor) {
0295                 return false;
0296             }
0297             QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize());
0298             QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height()));
0299             if (const auto fixedSize = outputLayer->fixedSize()) {
0300                 if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) {
0301                     return false;
0302                 }
0303                 bufferSize = *fixedSize;
0304                 nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize());
0305             }
0306             outputLayer->setPosition(nativeCursorRect.topLeft());
0307             outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize));
0308             outputLayer->setSize(bufferSize);
0309             if (auto beginInfo = outputLayer->beginFrame()) {
0310                 const RenderTarget &renderTarget = beginInfo->renderTarget;
0311 
0312                 RenderLayer renderLayer(output->renderLoop());
0313                 renderLayer.setDelegate(std::make_unique<SceneDelegate>(m_cursorScene.get(), output));
0314                 renderLayer.setOutputLayer(outputLayer);
0315 
0316                 renderLayer.delegate()->prePaint();
0317                 renderLayer.delegate()->paint(renderTarget, infiniteRegion());
0318                 renderLayer.delegate()->postPaint();
0319 
0320                 if (!outputLayer->endFrame(infiniteRegion(), infiniteRegion())) {
0321                     return false;
0322                 }
0323             } else {
0324                 return false;
0325             }
0326             outputLayer->setEnabled(true);
0327             return output->updateCursorLayer();
0328         };
0329         if (renderHardwareCursor()) {
0330             cursorLayer->setVisible(false);
0331             return true;
0332         } else {
0333             if (outputLayer && outputLayer->isEnabled()) {
0334                 outputLayer->setEnabled(false);
0335                 output->updateCursorLayer();
0336             }
0337             cursorLayer->setVisible(cursor->isOnOutput(output));
0338             cursorLayer->setGeometry(outputLocalRect);
0339             cursorLayer->addRepaintFull();
0340             return false;
0341         }
0342     };
0343     auto moveCursorLayer = [this, output, cursorLayer, updateCursorLayer]() {
0344         const Cursor *cursor = Cursors::self()->currentCursor();
0345         const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
0346         const auto outputLayer = m_backend->cursorLayer(output);
0347         bool hardwareCursor = false;
0348         if (outputLayer) {
0349             if (outputLayer->isEnabled()) {
0350                 const QRectF nativeCursorRect = output->transform()
0351                                                     .map(QRectF(outputLocalRect.topLeft() * output->scale(), outputLayer->size()), output->pixelSize());
0352                 outputLayer->setPosition(nativeCursorRect.topLeft());
0353                 hardwareCursor = output->updateCursorLayer();
0354             } else if (!cursorLayer->isVisible() && !forceSoftwareCursor) {
0355                 // this is for the case that the cursor wasn't visible because it was on a different output before
0356                 hardwareCursor = updateCursorLayer();
0357             }
0358         }
0359         cursorLayer->setVisible(cursor->isOnOutput(output) && !hardwareCursor);
0360         cursorLayer->setGeometry(outputLocalRect);
0361         cursorLayer->addRepaintFull();
0362     };
0363     updateCursorLayer();
0364     connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer);
0365     connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer);
0366     connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer);
0367     connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer);
0368 
0369     addSuperLayer(workspaceLayer);
0370 }
0371 
0372 void WaylandCompositor::removeOutput(Output *output)
0373 {
0374     if (output->isPlaceholder()) {
0375         return;
0376     }
0377     removeSuperLayer(m_superlayers[output->renderLoop()]);
0378 }
0379 
0380 } // namespace KWin
0381 
0382 #include "moc_compositor_wayland.cpp"