File indexing completed on 2024-04-28 16:48:40

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 #include "composite.h"
0010 
0011 #include <config-kwin.h>
0012 
0013 #include "core/output.h"
0014 #include "core/outputbackend.h"
0015 #include "core/outputlayer.h"
0016 #include "core/overlaywindow.h"
0017 #include "core/renderlayer.h"
0018 #include "core/renderloop.h"
0019 #include "cursordelegate_opengl.h"
0020 #include "cursordelegate_qpainter.h"
0021 #include "dbusinterface.h"
0022 #include "decorations/decoratedclient.h"
0023 #include "deleted.h"
0024 #include "effects.h"
0025 #include "ftrace.h"
0026 #include "internalwindow.h"
0027 #include "openglbackend.h"
0028 #include "qpainterbackend.h"
0029 #include "scene/cursorscene.h"
0030 #include "scene/itemrenderer_opengl.h"
0031 #include "scene/itemrenderer_qpainter.h"
0032 #include "scene/surfaceitem_x11.h"
0033 #include "scene/workspacescene_opengl.h"
0034 #include "scene/workspacescene_qpainter.h"
0035 #include "shadow.h"
0036 #include "unmanaged.h"
0037 #include "useractions.h"
0038 #include "utils/common.h"
0039 #include "utils/xcbutils.h"
0040 #include "wayland/surface_interface.h"
0041 #include "wayland_server.h"
0042 #include "workspace.h"
0043 #include "x11syncmanager.h"
0044 #include "x11window.h"
0045 
0046 #include <kwinglplatform.h>
0047 #include <kwingltexture.h>
0048 
0049 #include <KCrash>
0050 #include <KGlobalAccel>
0051 #include <KLocalizedString>
0052 #if KWIN_BUILD_NOTIFICATIONS
0053 #include <KNotification>
0054 #endif
0055 #include <KSelectionOwner>
0056 
0057 #include <QDateTime>
0058 #include <QFutureWatcher>
0059 #include <QMenu>
0060 #include <QOpenGLContext>
0061 #include <QQuickWindow>
0062 #include <QTextStream>
0063 #include <QTimerEvent>
0064 #include <QtConcurrentRun>
0065 
0066 #include <xcb/composite.h>
0067 #include <xcb/damage.h>
0068 
0069 #include <cstdio>
0070 
0071 Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
0072 
0073 namespace KWin
0074 {
0075 
0076 Compositor *Compositor::s_compositor = nullptr;
0077 Compositor *Compositor::self()
0078 {
0079     return s_compositor;
0080 }
0081 
0082 WaylandCompositor *WaylandCompositor::create(QObject *parent)
0083 {
0084     Q_ASSERT(!s_compositor);
0085     auto *compositor = new WaylandCompositor(parent);
0086     s_compositor = compositor;
0087     return compositor;
0088 }
0089 X11Compositor *X11Compositor::create(QObject *parent)
0090 {
0091     Q_ASSERT(!s_compositor);
0092     auto *compositor = new X11Compositor(parent);
0093     s_compositor = compositor;
0094     return compositor;
0095 }
0096 
0097 class CompositorSelectionOwner : public KSelectionOwner
0098 {
0099     Q_OBJECT
0100 public:
0101     CompositorSelectionOwner(const char *selection)
0102         : KSelectionOwner(selection, kwinApp()->x11Connection(), kwinApp()->x11RootWindow())
0103         , m_owning(false)
0104     {
0105         connect(this, &CompositorSelectionOwner::lostOwnership,
0106                 this, [this]() {
0107                     m_owning = false;
0108                 });
0109     }
0110     bool owning() const
0111     {
0112         return m_owning;
0113     }
0114     void setOwning(bool own)
0115     {
0116         m_owning = own;
0117     }
0118 
0119 private:
0120     bool m_owning;
0121 };
0122 
0123 Compositor::Compositor(QObject *workspace)
0124     : QObject(workspace)
0125 {
0126     connect(options, &Options::configChanged, this, &Compositor::configChanged);
0127     connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
0128 
0129     // 2 sec which should be enough to restart the compositor.
0130     static const int compositorLostMessageDelay = 2000;
0131 
0132     m_releaseSelectionTimer.setSingleShot(true);
0133     m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
0134     connect(&m_releaseSelectionTimer, &QTimer::timeout,
0135             this, &Compositor::releaseCompositorSelection);
0136 
0137     m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
0138     m_unusedSupportPropertyTimer.setSingleShot(true);
0139     connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
0140             this, &Compositor::deleteUnusedSupportProperties);
0141 
0142     // Delay the call to start by one event cycle.
0143     // The ctor of this class is invoked from the Workspace ctor, that means before
0144     // Workspace is completely constructed, so calling Workspace::self() would result
0145     // in undefined behavior. This is fixed by using a delayed invocation.
0146     QTimer::singleShot(0, this, &Compositor::start);
0147 
0148     connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::initializeX11);
0149     connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this, &Compositor::cleanupX11);
0150 
0151     // register DBus
0152     new CompositorDBusInterface(this);
0153     FTraceLogger::create();
0154 }
0155 
0156 Compositor::~Compositor()
0157 {
0158     deleteUnusedSupportProperties();
0159     destroyCompositorSelection();
0160     s_compositor = nullptr;
0161 }
0162 
0163 bool Compositor::attemptOpenGLCompositing()
0164 {
0165     // Some broken drivers crash on glXQuery() so to prevent constant KWin crashes:
0166     if (openGLCompositingIsBroken()) {
0167         qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use";
0168         return false;
0169     }
0170 
0171     createOpenGLSafePoint(OpenGLSafePoint::PreInit);
0172     auto safePointScope = qScopeGuard([this]() {
0173         createOpenGLSafePoint(OpenGLSafePoint::PostInit);
0174     });
0175 
0176     std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
0177     if (!backend) {
0178         return false;
0179     }
0180     if (!backend->isFailed()) {
0181         backend->init();
0182     }
0183     if (backend->isFailed()) {
0184         return false;
0185     }
0186 
0187     const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
0188     if (!forceEnv.isEmpty()) {
0189         if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
0190             qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
0191         } else {
0192             // OpenGL 2 disabled by environment variable
0193             return false;
0194         }
0195     } else {
0196         if (!backend->isDirectRendering()) {
0197             return false;
0198         }
0199         if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
0200             qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
0201             return false;
0202         }
0203     }
0204 
0205     // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
0206     if (!hasGLVersion(2, 0)) {
0207         qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
0208         return false;
0209     }
0210 
0211     m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
0212     m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
0213     m_backend = std::move(backend);
0214 
0215     // set strict binding
0216     if (options->isGlStrictBindingFollowsDriver()) {
0217         options->setGlStrictBinding(!GLPlatform::instance()->supports(LooseBinding));
0218     }
0219 
0220     qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
0221     return true;
0222 }
0223 
0224 bool Compositor::attemptQPainterCompositing()
0225 {
0226     std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend());
0227     if (!backend || backend->isFailed()) {
0228         return false;
0229     }
0230 
0231     m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get());
0232     m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>());
0233     m_backend = std::move(backend);
0234 
0235     qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized";
0236     return true;
0237 }
0238 
0239 bool Compositor::setupStart()
0240 {
0241     if (kwinApp()->isTerminating()) {
0242         // Don't start while KWin is terminating. An event to restart might be lingering
0243         // in the event queue due to graphics reset.
0244         return false;
0245     }
0246     if (m_state != State::Off) {
0247         return false;
0248     }
0249     m_state = State::Starting;
0250 
0251     options->reloadCompositingSettings(true);
0252 
0253     initializeX11();
0254 
0255     // There might still be a deleted around, needs to be cleared before
0256     // creating the scene (BUG 333275).
0257     if (Workspace::self()) {
0258         while (!Workspace::self()->deletedList().isEmpty()) {
0259             Workspace::self()->deletedList().first()->discard();
0260         }
0261     }
0262 
0263     Q_EMIT aboutToToggleCompositing();
0264 
0265     const QVector<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors();
0266     QVector<CompositingType> candidateCompositors;
0267 
0268     // If compositing has been restarted, try to use the last used compositing type.
0269     if (m_selectedCompositor != NoCompositing) {
0270         candidateCompositors.append(m_selectedCompositor);
0271     } else {
0272         candidateCompositors = availableCompositors;
0273 
0274         const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
0275         if (userConfigIt != candidateCompositors.end()) {
0276             candidateCompositors.erase(userConfigIt);
0277             candidateCompositors.prepend(options->compositingMode());
0278         } else {
0279             qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
0280         }
0281     }
0282 
0283     for (auto type : std::as_const(candidateCompositors)) {
0284         bool stop = false;
0285         switch (type) {
0286         case OpenGLCompositing:
0287             qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
0288             stop = attemptOpenGLCompositing();
0289             break;
0290         case QPainterCompositing:
0291             qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
0292             stop = attemptQPainterCompositing();
0293             break;
0294         case NoCompositing:
0295             qCDebug(KWIN_CORE) << "Starting without compositing...";
0296             stop = true;
0297             break;
0298         }
0299 
0300         if (stop) {
0301             break;
0302         }
0303     }
0304 
0305     if (!m_backend) {
0306         m_state = State::Off;
0307 
0308         if (auto *con = kwinApp()->x11Connection()) {
0309             xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
0310                                                 XCB_COMPOSITE_REDIRECT_MANUAL);
0311         }
0312         if (m_selectionOwner) {
0313             m_selectionOwner->setOwning(false);
0314             m_selectionOwner->release();
0315         }
0316         if (!availableCompositors.contains(NoCompositing)) {
0317             qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
0318             qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
0319             qApp->quit();
0320         }
0321         return false;
0322     }
0323 
0324     m_selectedCompositor = m_backend->compositingType();
0325 
0326     if (!Workspace::self() && m_backend && m_backend->compositingType() == QPainterCompositing) {
0327         // Force Software QtQuick on first startup with QPainter.
0328 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0329         QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
0330 #else
0331         QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
0332 #endif
0333     }
0334 
0335     Q_EMIT sceneCreated();
0336 
0337     return true;
0338 }
0339 
0340 void Compositor::initializeX11()
0341 {
0342     xcb_connection_t *connection = kwinApp()->x11Connection();
0343     if (!connection) {
0344         return;
0345     }
0346 
0347     if (!m_selectionOwner) {
0348         m_selectionOwner = std::make_unique<CompositorSelectionOwner>("_NET_WM_CM_S0");
0349         connect(m_selectionOwner.get(), &CompositorSelectionOwner::lostOwnership, this, &Compositor::stop);
0350     }
0351     if (!m_selectionOwner->owning()) {
0352         // Force claim ownership.
0353         m_selectionOwner->claim(true);
0354         m_selectionOwner->setOwning(true);
0355     }
0356 
0357     xcb_composite_redirect_subwindows(connection, kwinApp()->x11RootWindow(),
0358                                       XCB_COMPOSITE_REDIRECT_MANUAL);
0359 }
0360 
0361 void Compositor::cleanupX11()
0362 {
0363     m_selectionOwner.reset();
0364 }
0365 
0366 void Compositor::startupWithWorkspace()
0367 {
0368     Q_ASSERT(m_scene);
0369     m_scene->initialize();
0370     m_cursorScene->initialize();
0371 
0372     const QList<Output *> outputs = workspace()->outputs();
0373     if (kwinApp()->operationMode() == Application::OperationModeX11) {
0374         auto workspaceLayer = new RenderLayer(outputs.constFirst()->renderLoop());
0375         workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get()));
0376         workspaceLayer->setGeometry(workspace()->geometry());
0377         connect(workspace(), &Workspace::geometryChanged, workspaceLayer, [workspaceLayer]() {
0378             workspaceLayer->setGeometry(workspace()->geometry());
0379         });
0380         addSuperLayer(workspaceLayer);
0381     } else {
0382         for (Output *output : outputs) {
0383             addOutput(output);
0384         }
0385         connect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput);
0386         connect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput);
0387     }
0388 
0389     m_state = State::On;
0390 
0391     for (X11Window *window : Workspace::self()->clientList()) {
0392         window->setupCompositing();
0393     }
0394     for (Unmanaged *window : Workspace::self()->unmanagedList()) {
0395         window->setupCompositing();
0396     }
0397     for (InternalWindow *window : workspace()->internalWindows()) {
0398         window->setupCompositing();
0399     }
0400 
0401     if (auto *server = waylandServer()) {
0402         const auto windows = server->windows();
0403         for (Window *window : windows) {
0404             window->setupCompositing();
0405         }
0406     }
0407 
0408     // Sets also the 'effects' pointer.
0409     kwinApp()->createEffectsHandler(this, m_scene.get());
0410 
0411     Q_EMIT compositingToggled(true);
0412 
0413     if (m_releaseSelectionTimer.isActive()) {
0414         m_releaseSelectionTimer.stop();
0415     }
0416 }
0417 
0418 Output *Compositor::findOutput(RenderLoop *loop) const
0419 {
0420     const auto outputs = workspace()->outputs();
0421     for (Output *output : outputs) {
0422         if (output->renderLoop() == loop) {
0423             return output;
0424         }
0425     }
0426     return nullptr;
0427 }
0428 
0429 void Compositor::addOutput(Output *output)
0430 {
0431     Q_ASSERT(kwinApp()->operationMode() != Application::OperationModeX11);
0432 
0433     auto workspaceLayer = new RenderLayer(output->renderLoop());
0434     workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output));
0435     workspaceLayer->setGeometry(output->rect());
0436     connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() {
0437         workspaceLayer->setGeometry(output->rect());
0438     });
0439 
0440     auto cursorLayer = new RenderLayer(output->renderLoop());
0441     cursorLayer->setVisible(false);
0442     if (m_backend->compositingType() == OpenGLCompositing) {
0443         cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>());
0444     } else {
0445         cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>());
0446     }
0447     cursorLayer->setParent(workspaceLayer);
0448     cursorLayer->setSuperlayer(workspaceLayer);
0449 
0450     auto updateCursorLayer = [output, cursorLayer]() {
0451         const Cursor *cursor = Cursors::self()->currentCursor();
0452         const QRect layerRect = output->mapFromGlobal(cursor->geometry());
0453         bool usesHardwareCursor = false;
0454         if (!Cursors::self()->isCursorHidden()) {
0455             usesHardwareCursor = output->setCursor(cursor->source()) && output->moveCursor(layerRect.topLeft());
0456         } else {
0457             usesHardwareCursor = output->setCursor(nullptr);
0458         }
0459         cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor);
0460         cursorLayer->setGeometry(layerRect);
0461         cursorLayer->addRepaintFull();
0462     };
0463     auto moveCursorLayer = [output, cursorLayer]() {
0464         const Cursor *cursor = Cursors::self()->currentCursor();
0465         const QRect layerRect = output->mapFromGlobal(cursor->geometry());
0466         const bool usesHardwareCursor = output->moveCursor(layerRect.topLeft());
0467         cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor);
0468         cursorLayer->setGeometry(layerRect);
0469         cursorLayer->addRepaintFull();
0470     };
0471     updateCursorLayer();
0472     connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer);
0473     connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer);
0474     connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer);
0475     connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer);
0476 
0477     addSuperLayer(workspaceLayer);
0478 }
0479 
0480 void Compositor::removeOutput(Output *output)
0481 {
0482     removeSuperLayer(m_superlayers[output->renderLoop()]);
0483 }
0484 
0485 void Compositor::addSuperLayer(RenderLayer *layer)
0486 {
0487     m_superlayers.insert(layer->loop(), layer);
0488     connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
0489 }
0490 
0491 void Compositor::removeSuperLayer(RenderLayer *layer)
0492 {
0493     m_superlayers.remove(layer->loop());
0494     disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
0495     delete layer;
0496 }
0497 
0498 void Compositor::scheduleRepaint()
0499 {
0500     for (auto it = m_superlayers.constBegin(); it != m_superlayers.constEnd(); ++it) {
0501         it.key()->scheduleRepaint();
0502     }
0503 }
0504 
0505 void Compositor::stop()
0506 {
0507     if (m_state == State::Off || m_state == State::Stopping) {
0508         return;
0509     }
0510     m_state = State::Stopping;
0511     Q_EMIT aboutToToggleCompositing();
0512 
0513     m_releaseSelectionTimer.start();
0514 
0515     // Some effects might need access to effect windows when they are about to
0516     // be destroyed, for example to unreference deleted windows, so we have to
0517     // make sure that effect windows outlive effects.
0518     delete effects;
0519     effects = nullptr;
0520 
0521     if (Workspace::self()) {
0522         for (X11Window *window : Workspace::self()->clientList()) {
0523             window->finishCompositing();
0524         }
0525         for (Unmanaged *window : Workspace::self()->unmanagedList()) {
0526             window->finishCompositing();
0527         }
0528         for (InternalWindow *window : workspace()->internalWindows()) {
0529             window->finishCompositing();
0530         }
0531         if (auto *con = kwinApp()->x11Connection()) {
0532             xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(),
0533                                                 XCB_COMPOSITE_REDIRECT_MANUAL);
0534         }
0535         while (!workspace()->deletedList().isEmpty()) {
0536             workspace()->deletedList().first()->discard();
0537         }
0538 
0539         disconnect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput);
0540         disconnect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput);
0541     }
0542 
0543     if (waylandServer()) {
0544         const QList<Window *> toFinishCompositing = waylandServer()->windows();
0545         for (Window *window : toFinishCompositing) {
0546             window->finishCompositing();
0547         }
0548     }
0549 
0550     const auto superlayers = m_superlayers;
0551     for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
0552         removeSuperLayer(*it);
0553     }
0554 
0555     m_scene.reset();
0556     m_cursorScene.reset();
0557     m_backend.reset();
0558 
0559     m_state = State::Off;
0560     Q_EMIT compositingToggled(false);
0561 }
0562 
0563 void Compositor::destroyCompositorSelection()
0564 {
0565     m_selectionOwner.reset();
0566 }
0567 
0568 void Compositor::releaseCompositorSelection()
0569 {
0570     switch (m_state) {
0571     case State::On:
0572         // We are compositing at the moment. Don't release.
0573         break;
0574     case State::Off:
0575         if (m_selectionOwner) {
0576             qCDebug(KWIN_CORE) << "Releasing compositor selection";
0577             m_selectionOwner->setOwning(false);
0578             m_selectionOwner->release();
0579         }
0580         break;
0581     case State::Starting:
0582     case State::Stopping:
0583         // Still starting or shutting down the compositor. Starting might fail
0584         // or after stopping a restart might follow. So test again later on.
0585         m_releaseSelectionTimer.start();
0586         break;
0587     }
0588 }
0589 
0590 void Compositor::keepSupportProperty(xcb_atom_t atom)
0591 {
0592     m_unusedSupportProperties.removeAll(atom);
0593 }
0594 
0595 void Compositor::removeSupportProperty(xcb_atom_t atom)
0596 {
0597     m_unusedSupportProperties << atom;
0598     m_unusedSupportPropertyTimer.start();
0599 }
0600 
0601 void Compositor::deleteUnusedSupportProperties()
0602 {
0603     if (m_state == State::Starting || m_state == State::Stopping) {
0604         // Currently still maybe restarting the compositor.
0605         m_unusedSupportPropertyTimer.start();
0606         return;
0607     }
0608     if (auto *con = kwinApp()->x11Connection()) {
0609         for (const xcb_atom_t &atom : std::as_const(m_unusedSupportProperties)) {
0610             // remove property from root window
0611             xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
0612         }
0613         m_unusedSupportProperties.clear();
0614     }
0615 }
0616 
0617 void Compositor::configChanged()
0618 {
0619     reinitialize();
0620 }
0621 
0622 void Compositor::reinitialize()
0623 {
0624     // Reparse config. Config options will be reloaded by start()
0625     kwinApp()->config()->reparseConfiguration();
0626 
0627     // Restart compositing
0628     stop();
0629     start();
0630 
0631     if (effects) { // start() may fail
0632         effects->reconfigure();
0633     }
0634 }
0635 
0636 void Compositor::handleFrameRequested(RenderLoop *renderLoop)
0637 {
0638     composite(renderLoop);
0639 }
0640 
0641 void Compositor::composite(RenderLoop *renderLoop)
0642 {
0643     if (m_backend->checkGraphicsReset()) {
0644         qCDebug(KWIN_CORE) << "Graphics reset occurred";
0645 #if KWIN_BUILD_NOTIFICATIONS
0646         KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
0647 #endif
0648         reinitialize();
0649         return;
0650     }
0651 
0652     Output *output = findOutput(renderLoop);
0653     OutputLayer *primaryLayer = m_backend->primaryLayer(output);
0654     fTraceDuration("Paint (", output->name(), ")");
0655 
0656     RenderLayer *superLayer = m_superlayers[renderLoop];
0657     prePaintPass(superLayer);
0658     superLayer->setOutputLayer(primaryLayer);
0659 
0660     SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
0661     renderLoop->setFullscreenSurface(scanoutCandidate);
0662     output->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None);
0663 
0664     renderLoop->beginFrame();
0665     bool directScanout = false;
0666     if (scanoutCandidate) {
0667         const auto sublayers = superLayer->sublayers();
0668         const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
0669             return sublayer->isVisible();
0670         });
0671         if (scanoutPossible && !output->directScanoutInhibited()) {
0672             directScanout = primaryLayer->scanout(scanoutCandidate);
0673         }
0674     }
0675 
0676     if (!directScanout) {
0677         QRegion surfaceDamage = primaryLayer->repaints();
0678         primaryLayer->resetRepaints();
0679         preparePaintPass(superLayer, &surfaceDamage);
0680 
0681         if (auto beginInfo = primaryLayer->beginFrame()) {
0682             auto &[renderTarget, repaint] = beginInfo.value();
0683             renderTarget.setDevicePixelRatio(output->scale());
0684 
0685             const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect());
0686             primaryLayer->aboutToStartPainting(bufferDamage);
0687 
0688             paintPass(superLayer, &renderTarget, bufferDamage);
0689             primaryLayer->endFrame(bufferDamage, surfaceDamage);
0690         }
0691     }
0692 
0693     postPaintPass(superLayer);
0694     renderLoop->endFrame();
0695 
0696     m_backend->present(output);
0697 
0698     // TODO: Put it inside the cursor layer once the cursor layer can be backed by a real output layer.
0699     if (waylandServer()) {
0700         const std::chrono::milliseconds frameTime =
0701             std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
0702 
0703         if (!Cursors::self()->isCursorHidden()) {
0704             Cursor *cursor = Cursors::self()->currentCursor();
0705             if (cursor->geometry().intersects(output->geometry())) {
0706                 cursor->markAsRendered(frameTime);
0707             }
0708         }
0709     }
0710 }
0711 
0712 void Compositor::prePaintPass(RenderLayer *layer)
0713 {
0714     layer->delegate()->prePaint();
0715     const auto sublayers = layer->sublayers();
0716     for (RenderLayer *sublayer : sublayers) {
0717         prePaintPass(sublayer);
0718     }
0719 }
0720 
0721 void Compositor::postPaintPass(RenderLayer *layer)
0722 {
0723     layer->delegate()->postPaint();
0724     const auto sublayers = layer->sublayers();
0725     for (RenderLayer *sublayer : sublayers) {
0726         postPaintPass(sublayer);
0727     }
0728 }
0729 
0730 void Compositor::preparePaintPass(RenderLayer *layer, QRegion *repaint)
0731 {
0732     // TODO: Cull opaque region.
0733     *repaint += layer->mapToGlobal(layer->repaints() + layer->delegate()->repaints());
0734     layer->resetRepaints();
0735     const auto sublayers = layer->sublayers();
0736     for (RenderLayer *sublayer : sublayers) {
0737         if (sublayer->isVisible()) {
0738             preparePaintPass(sublayer, repaint);
0739         }
0740     }
0741 }
0742 
0743 void Compositor::paintPass(RenderLayer *layer, RenderTarget *target, const QRegion &region)
0744 {
0745     layer->delegate()->paint(target, region);
0746 
0747     const auto sublayers = layer->sublayers();
0748     for (RenderLayer *sublayer : sublayers) {
0749         if (sublayer->isVisible()) {
0750             paintPass(sublayer, target, region);
0751         }
0752     }
0753 }
0754 
0755 bool Compositor::isActive()
0756 {
0757     return m_state == State::On;
0758 }
0759 
0760 bool Compositor::compositingPossible() const
0761 {
0762     return true;
0763 }
0764 
0765 QString Compositor::compositingNotPossibleReason() const
0766 {
0767     return QString();
0768 }
0769 
0770 bool Compositor::openGLCompositingIsBroken() const
0771 {
0772     return false;
0773 }
0774 
0775 void Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
0776 {
0777 }
0778 
0779 WaylandCompositor::WaylandCompositor(QObject *parent)
0780     : Compositor(parent)
0781 {
0782     connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
0783             this, &WaylandCompositor::destroyCompositorSelection);
0784 }
0785 
0786 WaylandCompositor::~WaylandCompositor()
0787 {
0788     Q_EMIT aboutToDestroy();
0789     stop(); // this can't be called in the destructor of Compositor
0790 }
0791 
0792 void WaylandCompositor::toggleCompositing()
0793 {
0794     // For the shortcut. Not possible on Wayland because we always composite.
0795 }
0796 
0797 void WaylandCompositor::start()
0798 {
0799     if (!Compositor::setupStart()) {
0800         // Internal setup failed, abort.
0801         return;
0802     }
0803 
0804     if (Workspace::self()) {
0805         startupWithWorkspace();
0806     } else {
0807         connect(kwinApp(), &Application::workspaceCreated,
0808                 this, &WaylandCompositor::startupWithWorkspace);
0809     }
0810 }
0811 
0812 X11Compositor::X11Compositor(QObject *parent)
0813     : Compositor(parent)
0814     , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
0815 {
0816     if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
0817         m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
0818     }
0819 }
0820 
0821 X11Compositor::~X11Compositor()
0822 {
0823     Q_EMIT aboutToDestroy();
0824     if (m_openGLFreezeProtectionThread) {
0825         m_openGLFreezeProtectionThread->quit();
0826         m_openGLFreezeProtectionThread->wait();
0827     }
0828     stop(); // this can't be called in the destructor of Compositor
0829 }
0830 
0831 X11SyncManager *X11Compositor::syncManager() const
0832 {
0833     return m_syncManager.get();
0834 }
0835 
0836 void X11Compositor::toggleCompositing()
0837 {
0838     if (m_suspended) {
0839         // Direct user call; clear all bits.
0840         resume(AllReasonSuspend);
0841     } else {
0842         // But only set the user one (sufficient to suspend).
0843         suspend(UserSuspend);
0844     }
0845 }
0846 
0847 void X11Compositor::reinitialize()
0848 {
0849     // Resume compositing if suspended.
0850     m_suspended = NoReasonSuspend;
0851     Compositor::reinitialize();
0852 }
0853 
0854 void X11Compositor::configChanged()
0855 {
0856     if (m_suspended) {
0857         stop();
0858         return;
0859     }
0860     Compositor::configChanged();
0861 }
0862 
0863 void X11Compositor::suspend(X11Compositor::SuspendReason reason)
0864 {
0865     Q_ASSERT(reason != NoReasonSuspend);
0866     m_suspended |= reason;
0867 
0868     if (reason & ScriptSuspend) {
0869         // When disabled show a shortcut how the user can get back compositing.
0870         const auto shortcuts = KGlobalAccel::self()->shortcut(
0871             workspace()->findChild<QAction *>(QStringLiteral("Suspend Compositing")));
0872         if (!shortcuts.isEmpty()) {
0873             // Display notification only if there is the shortcut.
0874             const QString message =
0875                 i18n("Desktop effects have been suspended by another application.<br/>"
0876                      "You can resume using the '%1' shortcut.",
0877                      shortcuts.first().toString(QKeySequence::NativeText));
0878 #if KWIN_BUILD_NOTIFICATIONS
0879             KNotification::event(QStringLiteral("compositingsuspendeddbus"), message);
0880 #endif
0881         }
0882     }
0883     stop();
0884 }
0885 
0886 void X11Compositor::resume(X11Compositor::SuspendReason reason)
0887 {
0888     Q_ASSERT(reason != NoReasonSuspend);
0889     m_suspended &= ~reason;
0890     start();
0891 }
0892 
0893 void X11Compositor::start()
0894 {
0895     if (m_suspended) {
0896         QStringList reasons;
0897         if (m_suspended & UserSuspend) {
0898             reasons << QStringLiteral("Disabled by User");
0899         }
0900         if (m_suspended & BlockRuleSuspend) {
0901             reasons << QStringLiteral("Disabled by Window");
0902         }
0903         if (m_suspended & ScriptSuspend) {
0904             reasons << QStringLiteral("Disabled by Script");
0905         }
0906         qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
0907         return;
0908     } else if (!compositingPossible()) {
0909         qCWarning(KWIN_CORE) << "Compositing is not possible";
0910         return;
0911     }
0912     if (!Compositor::setupStart()) {
0913         // Internal setup failed, abort.
0914         return;
0915     }
0916     startupWithWorkspace();
0917     m_syncManager.reset(X11SyncManager::create());
0918 }
0919 
0920 void X11Compositor::stop()
0921 {
0922     m_syncManager.reset();
0923     Compositor::stop();
0924 }
0925 
0926 void X11Compositor::composite(RenderLoop *renderLoop)
0927 {
0928     if (backend()->overlayWindow() && !isOverlayWindowVisible()) {
0929         // Return since nothing is visible.
0930         return;
0931     }
0932 
0933     QList<Window *> windows = workspace()->stackingOrder();
0934     QList<SurfaceItemX11 *> dirtyItems;
0935 
0936     // Reset the damage state of each window and fetch the damage region
0937     // without waiting for a reply
0938     for (Window *window : std::as_const(windows)) {
0939         SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
0940         if (surfaceItem->fetchDamage()) {
0941             dirtyItems.append(surfaceItem);
0942         }
0943     }
0944 
0945     if (dirtyItems.count() > 0) {
0946         if (m_syncManager) {
0947             m_syncManager->triggerFence();
0948         }
0949         xcb_flush(kwinApp()->x11Connection());
0950     }
0951 
0952     // Get the replies
0953     for (SurfaceItemX11 *item : std::as_const(dirtyItems)) {
0954         item->waitForDamage();
0955     }
0956 
0957     if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) {
0958         createOpenGLSafePoint(OpenGLSafePoint::PreFrame);
0959     }
0960 
0961     Compositor::composite(renderLoop);
0962 
0963     if (m_syncManager) {
0964         if (!m_syncManager->endFrame()) {
0965             qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
0966             qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
0967             m_syncManager.reset();
0968         }
0969     }
0970 
0971     if (m_framesToTestForSafety > 0) {
0972         if (backend()->compositingType() & OpenGLCompositing) {
0973             createOpenGLSafePoint(OpenGLSafePoint::PostFrame);
0974         }
0975         m_framesToTestForSafety--;
0976         if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) {
0977             createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame);
0978         }
0979     }
0980 }
0981 
0982 bool X11Compositor::checkForOverlayWindow(WId w) const
0983 {
0984     if (!backend()) {
0985         // No backend, so it cannot be the overlay window.
0986         return false;
0987     }
0988     if (!backend()->overlayWindow()) {
0989         // No overlay window, it cannot be the overlay.
0990         return false;
0991     }
0992     // Compare the window ID's.
0993     return w == backend()->overlayWindow()->window();
0994 }
0995 
0996 bool X11Compositor::isOverlayWindowVisible() const
0997 {
0998     if (!backend()) {
0999         return false;
1000     }
1001     if (!backend()->overlayWindow()) {
1002         return false;
1003     }
1004     return backend()->overlayWindow()->isVisible();
1005 }
1006 
1007 void X11Compositor::updateClientCompositeBlocking(X11Window *c)
1008 {
1009     if (c) {
1010         if (c->isBlockingCompositing()) {
1011             // Do NOT attempt to call suspend(true) from within the eventchain!
1012             if (!(m_suspended & BlockRuleSuspend)) {
1013                 QMetaObject::invokeMethod(
1014                     this, [this]() {
1015                         suspend(BlockRuleSuspend);
1016                     },
1017                     Qt::QueuedConnection);
1018             }
1019         }
1020     } else if (m_suspended & BlockRuleSuspend) {
1021         // If !c we just check if we can resume in case a blocking client was lost.
1022         bool shouldResume = true;
1023 
1024         for (auto it = Workspace::self()->clientList().constBegin();
1025              it != Workspace::self()->clientList().constEnd(); ++it) {
1026             if ((*it)->isBlockingCompositing()) {
1027                 shouldResume = false;
1028                 break;
1029             }
1030         }
1031         if (shouldResume) {
1032             // Do NOT attempt to call suspend(false) from within the eventchain!
1033             QMetaObject::invokeMethod(
1034                 this, [this]() {
1035                     resume(BlockRuleSuspend);
1036                 },
1037                 Qt::QueuedConnection);
1038         }
1039     }
1040 }
1041 
1042 X11Compositor *X11Compositor::self()
1043 {
1044     return qobject_cast<X11Compositor *>(Compositor::self());
1045 }
1046 
1047 bool X11Compositor::openGLCompositingIsBroken() const
1048 {
1049     auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0);
1050     if (timestamp > 0) {
1051         if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) {
1052             return true;
1053         }
1054     }
1055 
1056     return false;
1057 }
1058 
1059 QString X11Compositor::compositingNotPossibleReason() const
1060 {
1061     // first off, check whether we figured that we'll crash on detection because of a buggy driver
1062     KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
1063     if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
1064         return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
1065                     "This was most likely due to a driver bug."
1066                     "<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
1067                     "you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>");
1068     }
1069 
1070     if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
1071         return i18n("Required X extensions (XComposite and XDamage) are not available.");
1072     }
1073     if (!Xcb::Extensions::self()->hasGlx()) {
1074         return i18n("GLX/OpenGL is not available.");
1075     }
1076     return QString();
1077 }
1078 
1079 bool X11Compositor::compositingPossible() const
1080 {
1081     // first off, check whether we figured that we'll crash on detection because of a buggy driver
1082     KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
1083     if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
1084         qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes.";
1085         return false;
1086     }
1087 
1088     if (!Xcb::Extensions::self()->isCompositeAvailable()) {
1089         qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available";
1090         return false;
1091     }
1092     if (!Xcb::Extensions::self()->isDamageAvailable()) {
1093         qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available";
1094         return false;
1095     }
1096     if (Xcb::Extensions::self()->hasGlx()) {
1097         return true;
1098     }
1099     if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
1100         return true;
1101     } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
1102         return true;
1103     }
1104     qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support";
1105     return false;
1106 }
1107 
1108 void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
1109 {
1110     auto group = KConfigGroup(kwinApp()->config(), "Compositing");
1111     switch (safePoint) {
1112     case OpenGLSafePoint::PreInit:
1113         // Explicitly write the failure timestamp so that if we crash during
1114         // OpenGL init, we know we should not try again.
1115         group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
1116         group.sync();
1117         // Deliberately continue with PreFrame
1118         Q_FALLTHROUGH();
1119     case OpenGLSafePoint::PreFrame:
1120         if (m_openGLFreezeProtectionThread == nullptr) {
1121             Q_ASSERT(m_openGLFreezeProtection == nullptr);
1122             m_openGLFreezeProtectionThread = std::make_unique<QThread>();
1123             m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
1124             m_openGLFreezeProtectionThread->start();
1125             m_openGLFreezeProtection = std::make_unique<QTimer>();
1126             m_openGLFreezeProtection->setInterval(15000);
1127             m_openGLFreezeProtection->setSingleShot(true);
1128             m_openGLFreezeProtection->start();
1129             const QString configName = kwinApp()->config()->name();
1130             m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get());
1131             connect(
1132                 m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(),
1133                 [configName] {
1134                     auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing");
1135                     group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
1136                     group.sync();
1137                     KCrash::setDrKonqiEnabled(false);
1138                     qFatal("Freeze in OpenGL initialization detected");
1139                 },
1140                 Qt::DirectConnection);
1141         } else {
1142             Q_ASSERT(m_openGLFreezeProtection);
1143             QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection);
1144         }
1145         break;
1146     case OpenGLSafePoint::PostInit:
1147         group.deleteEntry(QLatin1String("LastFailureTimestamp"));
1148         group.sync();
1149         // Deliberately continue with PostFrame
1150         Q_FALLTHROUGH();
1151     case OpenGLSafePoint::PostFrame:
1152         QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection);
1153         break;
1154     case OpenGLSafePoint::PostLastGuardedFrame:
1155         m_openGLFreezeProtectionThread->quit();
1156         m_openGLFreezeProtectionThread->wait();
1157         m_openGLFreezeProtectionThread.reset();
1158         m_openGLFreezeProtection.reset();
1159         break;
1160     }
1161 }
1162 
1163 }
1164 
1165 // included for CompositorSelectionOwner
1166 #include "composite.moc"