From 6dd6e176e39bbd65dbe297455445a176c7bdba3c Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 31 Aug 2023 15:44:50 +0300 Subject: [PATCH] Move X11Compositor and WaylandCompositor in their own files --- autotests/integration/kwin_wayland_test.cpp | 2 +- src/CMakeLists.txt | 2 + src/compositor.cpp | 426 +------------------- src/compositor.h | 114 +----- src/compositor_wayland.cpp | 60 +++ src/compositor_wayland.h | 34 ++ src/compositor_x11.cpp | 398 ++++++++++++++++++ src/compositor_x11.h | 112 +++++ src/dbusinterface.cpp | 2 +- src/main_wayland.cpp | 2 +- src/scene/surfaceitem_x11.cpp | 2 +- src/workspace.cpp | 2 +- 12 files changed, 614 insertions(+), 542 deletions(-) create mode 100644 src/compositor_wayland.cpp create mode 100644 src/compositor_wayland.h create mode 100644 src/compositor_x11.cpp create mode 100644 src/compositor_x11.h diff --git a/autotests/integration/kwin_wayland_test.cpp b/autotests/integration/kwin_wayland_test.cpp index c439cc258f..e2bbef3587 100644 --- a/autotests/integration/kwin_wayland_test.cpp +++ b/autotests/integration/kwin_wayland_test.cpp @@ -9,7 +9,7 @@ #include "kwin_wayland_test.h" #include "backends/virtual/virtual_backend.h" -#include "compositor.h" +#include "compositor_wayland.h" #include "core/session.h" #include "effects.h" #include "inputmethod.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 506a736266..691dd3f132 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,8 @@ target_sources(kwin PRIVATE colors/colordevice.cpp colors/colormanager.cpp compositor.cpp + compositor_wayland.cpp + compositor_x11.cpp core/colorlut.cpp core/colorpipelinestage.cpp core/colortransformation.cpp diff --git a/src/compositor.cpp b/src/compositor.cpp index 026b9ac664..16d3820923 100644 --- a/src/compositor.cpp +++ b/src/compositor.cpp @@ -13,14 +13,11 @@ #include "core/output.h" #include "core/outputbackend.h" #include "core/outputlayer.h" -#include "core/overlaywindow.h" #include "core/renderlayer.h" #include "core/renderloop.h" #include "dbusinterface.h" -#include "decorations/decoratedclient.h" #include "effects.h" #include "ftrace.h" -#include "internalwindow.h" #include "platformsupport/scenes/opengl/openglbackend.h" #include "platformsupport/scenes/qpainter/qpainterbackend.h" #include "scene/cursordelegate_opengl.h" @@ -28,43 +25,24 @@ #include "scene/cursorscene.h" #include "scene/itemrenderer_opengl.h" #include "scene/itemrenderer_qpainter.h" -#include "scene/surfaceitem_x11.h" +#include "scene/surfaceitem.h" #include "scene/workspacescene_opengl.h" #include "scene/workspacescene_qpainter.h" -#include "useractions.h" #include "utils/common.h" -#include "utils/xcbutils.h" #include "wayland_server.h" #include "workspace.h" -#include "x11syncmanager.h" -#include "x11window.h" #include "libkwineffects/glplatform.h" -#include "libkwineffects/gltexture.h" -#include -#include #include #if KWIN_BUILD_NOTIFICATIONS #include #endif #include -#include -#include -#include -#include #include -#include -#include -#include #include -#include - -#include - -Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason) namespace KWin { @@ -75,21 +53,6 @@ Compositor *Compositor::self() return s_compositor; } -WaylandCompositor *WaylandCompositor::create(QObject *parent) -{ - Q_ASSERT(!s_compositor); - auto *compositor = new WaylandCompositor(parent); - s_compositor = compositor; - return compositor; -} -X11Compositor *X11Compositor::create(QObject *parent) -{ - Q_ASSERT(!s_compositor); - auto *compositor = new X11Compositor(parent); - s_compositor = compositor; - return compositor; -} - class CompositorSelectionOwner : public KSelectionOwner { Q_OBJECT @@ -808,392 +771,7 @@ void Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint) { } -WaylandCompositor::WaylandCompositor(QObject *parent) - : Compositor(parent) -{ - connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, - this, &WaylandCompositor::destroyCompositorSelection); -} - -WaylandCompositor::~WaylandCompositor() -{ - Q_EMIT aboutToDestroy(); - stop(); // this can't be called in the destructor of Compositor -} - -void WaylandCompositor::toggleCompositing() -{ - // For the shortcut. Not possible on Wayland because we always composite. -} - -void WaylandCompositor::start() -{ - if (!Compositor::setupStart()) { - // Internal setup failed, abort. - return; - } - - if (Workspace::self()) { - startupWithWorkspace(); - } else { - connect(kwinApp(), &Application::workspaceCreated, - this, &WaylandCompositor::startupWithWorkspace); - } -} - -X11Compositor::X11Compositor(QObject *parent) - : Compositor(parent) - , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) -{ - if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) { - m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED"); - } -} - -X11Compositor::~X11Compositor() -{ - Q_EMIT aboutToDestroy(); - if (m_openGLFreezeProtectionThread) { - m_openGLFreezeProtectionThread->quit(); - m_openGLFreezeProtectionThread->wait(); - } - stop(); // this can't be called in the destructor of Compositor -} - -X11SyncManager *X11Compositor::syncManager() const -{ - return m_syncManager.get(); -} - -void X11Compositor::toggleCompositing() -{ - if (m_suspended) { - // Direct user call; clear all bits. - resume(AllReasonSuspend); - } else { - // But only set the user one (sufficient to suspend). - suspend(UserSuspend); - } -} - -void X11Compositor::reinitialize() -{ - // Resume compositing if suspended. - m_suspended = NoReasonSuspend; - Compositor::reinitialize(); -} - -void X11Compositor::configChanged() -{ - if (m_suspended) { - stop(); - return; - } - Compositor::configChanged(); -} - -void X11Compositor::suspend(X11Compositor::SuspendReason reason) -{ - Q_ASSERT(reason != NoReasonSuspend); - m_suspended |= reason; - - if (reason & ScriptSuspend) { - // When disabled show a shortcut how the user can get back compositing. - const auto shortcuts = KGlobalAccel::self()->shortcut( - workspace()->findChild(QStringLiteral("Suspend Compositing"))); - if (!shortcuts.isEmpty()) { - // Display notification only if there is the shortcut. - const QString message = - i18n("Desktop effects have been suspended by another application.
" - "You can resume using the '%1' shortcut.", - shortcuts.first().toString(QKeySequence::NativeText)); -#if KWIN_BUILD_NOTIFICATIONS - KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); -#endif - } - } - stop(); -} - -void X11Compositor::resume(X11Compositor::SuspendReason reason) -{ - Q_ASSERT(reason != NoReasonSuspend); - m_suspended &= ~reason; - start(); -} - -void X11Compositor::start() -{ - if (m_suspended) { - QStringList reasons; - if (m_suspended & UserSuspend) { - reasons << QStringLiteral("Disabled by User"); - } - if (m_suspended & BlockRuleSuspend) { - reasons << QStringLiteral("Disabled by Window"); - } - if (m_suspended & ScriptSuspend) { - reasons << QStringLiteral("Disabled by Script"); - } - qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; - return; - } else if (!compositingPossible()) { - qCWarning(KWIN_CORE) << "Compositing is not possible"; - return; - } - if (!Compositor::setupStart()) { - // Internal setup failed, abort. - return; - } - startupWithWorkspace(); - m_syncManager.reset(X11SyncManager::create()); -} - -void X11Compositor::stop() -{ - m_syncManager.reset(); - Compositor::stop(); -} - -void X11Compositor::composite(RenderLoop *renderLoop) -{ - if (backend()->overlayWindow() && !isOverlayWindowVisible()) { - // Return since nothing is visible. - return; - } - - QList windows = workspace()->stackingOrder(); - QList dirtyItems; - - // Reset the damage state of each window and fetch the damage region - // without waiting for a reply - for (Window *window : std::as_const(windows)) { - SurfaceItemX11 *surfaceItem = static_cast(window->surfaceItem()); - if (surfaceItem->fetchDamage()) { - dirtyItems.append(surfaceItem); - } - } - - if (dirtyItems.count() > 0) { - if (m_syncManager) { - m_syncManager->triggerFence(); - } - xcb_flush(kwinApp()->x11Connection()); - } - - // Get the replies - for (SurfaceItemX11 *item : std::as_const(dirtyItems)) { - item->waitForDamage(); - } - - if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) { - createOpenGLSafePoint(OpenGLSafePoint::PreFrame); - } - - Compositor::composite(renderLoop); - - if (m_syncManager) { - if (!m_syncManager->endFrame()) { - qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream."; - qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized."; - m_syncManager.reset(); - } - } - - if (m_framesToTestForSafety > 0) { - if (backend()->compositingType() & OpenGLCompositing) { - createOpenGLSafePoint(OpenGLSafePoint::PostFrame); - } - m_framesToTestForSafety--; - if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) { - createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame); - } - } -} - -bool X11Compositor::checkForOverlayWindow(WId w) const -{ - if (!backend()) { - // No backend, so it cannot be the overlay window. - return false; - } - if (!backend()->overlayWindow()) { - // No overlay window, it cannot be the overlay. - return false; - } - // Compare the window ID's. - return w == backend()->overlayWindow()->window(); -} - -bool X11Compositor::isOverlayWindowVisible() const -{ - if (!backend()) { - return false; - } - if (!backend()->overlayWindow()) { - return false; - } - return backend()->overlayWindow()->isVisible(); -} - -void X11Compositor::updateClientCompositeBlocking(X11Window *c) -{ - if (c) { - if (c->isBlockingCompositing()) { - // Do NOT attempt to call suspend(true) from within the eventchain! - if (!(m_suspended & BlockRuleSuspend)) { - QMetaObject::invokeMethod( - this, [this]() { - suspend(BlockRuleSuspend); - }, - Qt::QueuedConnection); - } - } - } else if (m_suspended & BlockRuleSuspend) { - // If !c we just check if we can resume in case a blocking client was lost. - bool shouldResume = true; - - const auto windows = workspace()->windows(); - for (Window *window : windows) { - X11Window *x11Window = qobject_cast(window); - if (x11Window && x11Window->isBlockingCompositing()) { - shouldResume = false; - break; - } - } - if (shouldResume) { - // Do NOT attempt to call suspend(false) from within the eventchain! - QMetaObject::invokeMethod( - this, [this]() { - resume(BlockRuleSuspend); - }, - Qt::QueuedConnection); - } - } -} - -X11Compositor *X11Compositor::self() -{ - return qobject_cast(Compositor::self()); -} - -bool X11Compositor::openGLCompositingIsBroken() const -{ - auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0); - if (timestamp > 0) { - if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) { - return true; - } - } - - return false; -} - -QString X11Compositor::compositingNotPossibleReason() const -{ - // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { - return i18n("OpenGL compositing (the default) has crashed KWin in the past.
" - "This was most likely due to a driver bug." - "

If you think that you have meanwhile upgraded to a stable driver,
" - "you can reset this protection but be aware that this might result in an immediate crash!

"); - } - - if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { - return i18n("Required X extensions (XComposite and XDamage) are not available."); - } - if (!Xcb::Extensions::self()->hasGlx()) { - return i18n("GLX/OpenGL is not available."); - } - return QString(); -} - -bool X11Compositor::compositingPossible() const -{ - // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { - qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes."; - return false; - } - - if (!Xcb::Extensions::self()->isCompositeAvailable()) { - qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available"; - return false; - } - if (!Xcb::Extensions::self()->isDamageAvailable()) { - qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available"; - return false; - } - if (Xcb::Extensions::self()->hasGlx()) { - return true; - } - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - return true; - } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { - return true; - } - qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support"; - return false; -} - -void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint) -{ - auto group = KConfigGroup(kwinApp()->config(), "Compositing"); - switch (safePoint) { - case OpenGLSafePoint::PreInit: - // Explicitly write the failure timestamp so that if we crash during - // OpenGL init, we know we should not try again. - group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); - group.sync(); - // Deliberately continue with PreFrame - Q_FALLTHROUGH(); - case OpenGLSafePoint::PreFrame: - if (m_openGLFreezeProtectionThread == nullptr) { - Q_ASSERT(m_openGLFreezeProtection == nullptr); - m_openGLFreezeProtectionThread = std::make_unique(); - m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); - m_openGLFreezeProtectionThread->start(); - m_openGLFreezeProtection = std::make_unique(); - m_openGLFreezeProtection->setInterval(15000); - m_openGLFreezeProtection->setSingleShot(true); - m_openGLFreezeProtection->start(); - const QString configName = kwinApp()->config()->name(); - m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get()); - connect( - m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(), - [configName] { - auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing"); - group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); - group.sync(); - KCrash::setDrKonqiEnabled(false); - qFatal("Freeze in OpenGL initialization detected"); - }, - Qt::DirectConnection); - } else { - Q_ASSERT(m_openGLFreezeProtection); - QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection); - } - break; - case OpenGLSafePoint::PostInit: - group.deleteEntry(QLatin1String("LastFailureTimestamp")); - group.sync(); - // Deliberately continue with PostFrame - Q_FALLTHROUGH(); - case OpenGLSafePoint::PostFrame: - QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection); - break; - case OpenGLSafePoint::PostLastGuardedFrame: - m_openGLFreezeProtectionThread->quit(); - m_openGLFreezeProtectionThread->wait(); - m_openGLFreezeProtectionThread.reset(); - m_openGLFreezeProtection.reset(); - break; - } -} - -} +} // namespace KWin // included for CompositorSelectionOwner #include "compositor.moc" diff --git a/src/compositor.h b/src/compositor.h index 7e2e33f77c..2d7363926b 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -23,16 +23,11 @@ namespace KWin class Output; class CompositorSelectionOwner; class CursorScene; -class CursorView; class RenderBackend; class RenderLayer; class RenderLoop; class RenderTarget; class WorkspaceScene; -class Window; -class X11Window; -class X11SyncManager; -class RenderViewport; class KWIN_EXPORT Compositor : public QObject { @@ -212,111 +207,4 @@ private: CompositingType m_selectedCompositor = NoCompositing; }; -class KWIN_EXPORT WaylandCompositor final : public Compositor -{ - Q_OBJECT -public: - static WaylandCompositor *create(QObject *parent = nullptr); - ~WaylandCompositor() override; - - void toggleCompositing() override; - -protected: - void start() override; - -private: - explicit WaylandCompositor(QObject *parent); -}; - -class KWIN_EXPORT X11Compositor final : public Compositor -{ - Q_OBJECT -public: - enum SuspendReason { - NoReasonSuspend = 0, - UserSuspend = 1 << 0, - BlockRuleSuspend = 1 << 1, - ScriptSuspend = 1 << 2, - AllReasonSuspend = 0xff - }; - Q_DECLARE_FLAGS(SuspendReasons, SuspendReason) - Q_ENUM(SuspendReason) - Q_FLAG(SuspendReasons) - - static X11Compositor *create(QObject *parent = nullptr); - ~X11Compositor() override; - - X11SyncManager *syncManager() const; - - /** - * @brief Suspends the Compositor if it is currently active. - * - * Note: it is possible that the Compositor is not able to suspend. Use isActive to check - * whether the Compositor has been suspended. - * - * @return void - * @see resume - * @see isActive - */ - void suspend(SuspendReason reason); - - /** - * @brief Resumes the Compositor if it is currently suspended. - * - * Note: it is possible that the Compositor cannot be resumed, that is there might be Clients - * blocking the usage of Compositing or the Scene might be broken. Use isActive to check - * whether the Compositor has been resumed. Also check isCompositingPossible and - * isOpenGLBroken. - * - * Note: The starting of the Compositor can require some time and is partially done threaded. - * After this method returns the setup may not have been completed. - * - * @return void - * @see suspend - * @see isActive - * @see isCompositingPossible - * @see isOpenGLBroken - */ - void resume(SuspendReason reason); - - void toggleCompositing() override; - void reinitialize() override; - void configChanged() override; - bool compositingPossible() const override; - QString compositingNotPossibleReason() const override; - bool openGLCompositingIsBroken() const override; - void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; - - /** - * Checks whether @p w is the Scene's overlay window. - */ - bool checkForOverlayWindow(WId w) const; - - /** - * @returns Whether the Scene's Overlay X Window is visible. - */ - bool isOverlayWindowVisible() const; - - void updateClientCompositeBlocking(X11Window *client = nullptr); - - static X11Compositor *self(); - -protected: - void start() override; - void stop() override; - void composite(RenderLoop *renderLoop) override; - -private: - explicit X11Compositor(QObject *parent); - - std::unique_ptr m_openGLFreezeProtectionThread; - std::unique_ptr m_openGLFreezeProtection; - std::unique_ptr m_syncManager; - /** - * Whether the Compositor is currently suspended, 8 bits encoding the reason - */ - SuspendReasons m_suspended; - int m_framesToTestForSafety = 3; -}; - -} +} // namespace KWin diff --git a/src/compositor_wayland.cpp b/src/compositor_wayland.cpp new file mode 100644 index 0000000000..4e6a56695f --- /dev/null +++ b/src/compositor_wayland.cpp @@ -0,0 +1,60 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2006 Lubos Lunak + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "compositor_wayland.h" +#include "main.h" +#include "workspace.h" + +namespace KWin +{ + +WaylandCompositor *WaylandCompositor::create(QObject *parent) +{ + Q_ASSERT(!s_compositor); + auto *compositor = new WaylandCompositor(parent); + s_compositor = compositor; + return compositor; +} + +WaylandCompositor::WaylandCompositor(QObject *parent) + : Compositor(parent) +{ + connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, + this, &WaylandCompositor::destroyCompositorSelection); +} + +WaylandCompositor::~WaylandCompositor() +{ + Q_EMIT aboutToDestroy(); + stop(); // this can't be called in the destructor of Compositor +} + +void WaylandCompositor::toggleCompositing() +{ + // For the shortcut. Not possible on Wayland because we always composite. +} + +void WaylandCompositor::start() +{ + if (!Compositor::setupStart()) { + // Internal setup failed, abort. + return; + } + + if (Workspace::self()) { + startupWithWorkspace(); + } else { + connect(kwinApp(), &Application::workspaceCreated, + this, &WaylandCompositor::startupWithWorkspace); + } +} + +} // namespace KWin + +#include "moc_compositor_wayland.cpp" diff --git a/src/compositor_wayland.h b/src/compositor_wayland.h new file mode 100644 index 0000000000..12a5b49e7e --- /dev/null +++ b/src/compositor_wayland.h @@ -0,0 +1,34 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2011 Arthur Arlt + SPDX-FileCopyrightText: 2012 Martin Gräßlin + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "compositor.h" + +namespace KWin +{ + +class KWIN_EXPORT WaylandCompositor final : public Compositor +{ + Q_OBJECT +public: + static WaylandCompositor *create(QObject *parent = nullptr); + ~WaylandCompositor() override; + + void toggleCompositing() override; + +protected: + void start() override; + +private: + explicit WaylandCompositor(QObject *parent); +}; + +} // namespace KWin diff --git a/src/compositor_x11.cpp b/src/compositor_x11.cpp new file mode 100644 index 0000000000..d1a28c9bf6 --- /dev/null +++ b/src/compositor_x11.cpp @@ -0,0 +1,398 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2006 Lubos Lunak + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "compositor_x11.h" +#include "core/overlaywindow.h" +#include "core/renderbackend.h" +#include "options.h" +#include "scene/surfaceitem_x11.h" +#include "utils/common.h" +#include "utils/xcbutils.h" +#include "workspace.h" +#include "x11syncmanager.h" +#include "x11window.h" + +#include +#include +#include +#if KWIN_BUILD_NOTIFICATIONS +#include +#endif + +#include +#include +#include + +Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason) + +namespace KWin +{ + +X11Compositor *X11Compositor::create(QObject *parent) +{ + Q_ASSERT(!s_compositor); + auto *compositor = new X11Compositor(parent); + s_compositor = compositor; + return compositor; +} + +X11Compositor::X11Compositor(QObject *parent) + : Compositor(parent) + , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) +{ + if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) { + m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED"); + } +} + +X11Compositor::~X11Compositor() +{ + Q_EMIT aboutToDestroy(); + if (m_openGLFreezeProtectionThread) { + m_openGLFreezeProtectionThread->quit(); + m_openGLFreezeProtectionThread->wait(); + } + stop(); // this can't be called in the destructor of Compositor +} + +X11SyncManager *X11Compositor::syncManager() const +{ + return m_syncManager.get(); +} + +void X11Compositor::toggleCompositing() +{ + if (m_suspended) { + // Direct user call; clear all bits. + resume(AllReasonSuspend); + } else { + // But only set the user one (sufficient to suspend). + suspend(UserSuspend); + } +} + +void X11Compositor::reinitialize() +{ + // Resume compositing if suspended. + m_suspended = NoReasonSuspend; + Compositor::reinitialize(); +} + +void X11Compositor::configChanged() +{ + if (m_suspended) { + stop(); + return; + } + Compositor::configChanged(); +} + +void X11Compositor::suspend(X11Compositor::SuspendReason reason) +{ + Q_ASSERT(reason != NoReasonSuspend); + m_suspended |= reason; + + if (reason & ScriptSuspend) { + // When disabled show a shortcut how the user can get back compositing. + const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild(QStringLiteral("Suspend Compositing"))); + if (!shortcuts.isEmpty()) { + // Display notification only if there is the shortcut. + const QString message = + i18n("Desktop effects have been suspended by another application.
" + "You can resume using the '%1' shortcut.", + shortcuts.first().toString(QKeySequence::NativeText)); +#if KWIN_BUILD_NOTIFICATIONS + KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); +#endif + } + } + stop(); +} + +void X11Compositor::resume(X11Compositor::SuspendReason reason) +{ + Q_ASSERT(reason != NoReasonSuspend); + m_suspended &= ~reason; + start(); +} + +void X11Compositor::start() +{ + if (m_suspended) { + QStringList reasons; + if (m_suspended & UserSuspend) { + reasons << QStringLiteral("Disabled by User"); + } + if (m_suspended & BlockRuleSuspend) { + reasons << QStringLiteral("Disabled by Window"); + } + if (m_suspended & ScriptSuspend) { + reasons << QStringLiteral("Disabled by Script"); + } + qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; + return; + } else if (!compositingPossible()) { + qCWarning(KWIN_CORE) << "Compositing is not possible"; + return; + } + if (!Compositor::setupStart()) { + // Internal setup failed, abort. + return; + } + startupWithWorkspace(); + m_syncManager.reset(X11SyncManager::create()); +} + +void X11Compositor::stop() +{ + m_syncManager.reset(); + Compositor::stop(); +} + +void X11Compositor::composite(RenderLoop *renderLoop) +{ + if (backend()->overlayWindow() && !isOverlayWindowVisible()) { + // Return since nothing is visible. + return; + } + + QList windows = workspace()->stackingOrder(); + QList dirtyItems; + + // Reset the damage state of each window and fetch the damage region + // without waiting for a reply + for (Window *window : std::as_const(windows)) { + SurfaceItemX11 *surfaceItem = static_cast(window->surfaceItem()); + if (surfaceItem->fetchDamage()) { + dirtyItems.append(surfaceItem); + } + } + + if (dirtyItems.count() > 0) { + if (m_syncManager) { + m_syncManager->triggerFence(); + } + xcb_flush(kwinApp()->x11Connection()); + } + + // Get the replies + for (SurfaceItemX11 *item : std::as_const(dirtyItems)) { + item->waitForDamage(); + } + + if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) { + createOpenGLSafePoint(OpenGLSafePoint::PreFrame); + } + + Compositor::composite(renderLoop); + + if (m_syncManager) { + if (!m_syncManager->endFrame()) { + qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream."; + qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized."; + m_syncManager.reset(); + } + } + + if (m_framesToTestForSafety > 0) { + if (backend()->compositingType() & OpenGLCompositing) { + createOpenGLSafePoint(OpenGLSafePoint::PostFrame); + } + m_framesToTestForSafety--; + if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) { + createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame); + } + } +} + +bool X11Compositor::checkForOverlayWindow(WId w) const +{ + if (!backend()) { + // No backend, so it cannot be the overlay window. + return false; + } + if (!backend()->overlayWindow()) { + // No overlay window, it cannot be the overlay. + return false; + } + // Compare the window ID's. + return w == backend()->overlayWindow()->window(); +} + +bool X11Compositor::isOverlayWindowVisible() const +{ + if (!backend()) { + return false; + } + if (!backend()->overlayWindow()) { + return false; + } + return backend()->overlayWindow()->isVisible(); +} + +void X11Compositor::updateClientCompositeBlocking(X11Window *c) +{ + if (c) { + if (c->isBlockingCompositing()) { + // Do NOT attempt to call suspend(true) from within the eventchain! + if (!(m_suspended & BlockRuleSuspend)) { + QMetaObject::invokeMethod( + this, [this]() { + suspend(BlockRuleSuspend); + }, + Qt::QueuedConnection); + } + } + } else if (m_suspended & BlockRuleSuspend) { + // If !c we just check if we can resume in case a blocking client was lost. + bool shouldResume = true; + + const auto windows = workspace()->windows(); + for (Window *window : windows) { + X11Window *x11Window = qobject_cast(window); + if (x11Window && x11Window->isBlockingCompositing()) { + shouldResume = false; + break; + } + } + if (shouldResume) { + // Do NOT attempt to call suspend(false) from within the eventchain! + QMetaObject::invokeMethod( + this, [this]() { + resume(BlockRuleSuspend); + }, + Qt::QueuedConnection); + } + } +} + +X11Compositor *X11Compositor::self() +{ + return qobject_cast(Compositor::self()); +} + +bool X11Compositor::openGLCompositingIsBroken() const +{ + auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0); + if (timestamp > 0) { + if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) { + return true; + } + } + + return false; +} + +QString X11Compositor::compositingNotPossibleReason() const +{ + // first off, check whether we figured that we'll crash on detection because of a buggy driver + KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); + if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { + return i18n("OpenGL compositing (the default) has crashed KWin in the past.
" + "This was most likely due to a driver bug." + "

If you think that you have meanwhile upgraded to a stable driver,
" + "you can reset this protection but be aware that this might result in an immediate crash!

"); + } + + if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { + return i18n("Required X extensions (XComposite and XDamage) are not available."); + } + if (!Xcb::Extensions::self()->hasGlx()) { + return i18n("GLX/OpenGL is not available."); + } + return QString(); +} + +bool X11Compositor::compositingPossible() const +{ + // first off, check whether we figured that we'll crash on detection because of a buggy driver + KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); + if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { + qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes."; + return false; + } + + if (!Xcb::Extensions::self()->isCompositeAvailable()) { + qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available"; + return false; + } + if (!Xcb::Extensions::self()->isDamageAvailable()) { + qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available"; + return false; + } + if (Xcb::Extensions::self()->hasGlx()) { + return true; + } + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + return true; + } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { + return true; + } + qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support"; + return false; +} + +void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint) +{ + auto group = KConfigGroup(kwinApp()->config(), "Compositing"); + switch (safePoint) { + case OpenGLSafePoint::PreInit: + // Explicitly write the failure timestamp so that if we crash during + // OpenGL init, we know we should not try again. + group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); + group.sync(); + // Deliberately continue with PreFrame + Q_FALLTHROUGH(); + case OpenGLSafePoint::PreFrame: + if (m_openGLFreezeProtectionThread == nullptr) { + Q_ASSERT(m_openGLFreezeProtection == nullptr); + m_openGLFreezeProtectionThread = std::make_unique(); + m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); + m_openGLFreezeProtectionThread->start(); + m_openGLFreezeProtection = std::make_unique(); + m_openGLFreezeProtection->setInterval(15000); + m_openGLFreezeProtection->setSingleShot(true); + m_openGLFreezeProtection->start(); + const QString configName = kwinApp()->config()->name(); + m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get()); + connect( + m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(), + [configName] { + auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing"); + group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); + group.sync(); + KCrash::setDrKonqiEnabled(false); + qFatal("Freeze in OpenGL initialization detected"); + }, + Qt::DirectConnection); + } else { + Q_ASSERT(m_openGLFreezeProtection); + QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection); + } + break; + case OpenGLSafePoint::PostInit: + group.deleteEntry(QLatin1String("LastFailureTimestamp")); + group.sync(); + // Deliberately continue with PostFrame + Q_FALLTHROUGH(); + case OpenGLSafePoint::PostFrame: + QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection); + break; + case OpenGLSafePoint::PostLastGuardedFrame: + m_openGLFreezeProtectionThread->quit(); + m_openGLFreezeProtectionThread->wait(); + m_openGLFreezeProtectionThread.reset(); + m_openGLFreezeProtection.reset(); + break; + } +} + +} // namespace KWin + +#include "moc_compositor_x11.cpp" diff --git a/src/compositor_x11.h b/src/compositor_x11.h new file mode 100644 index 0000000000..f686399f89 --- /dev/null +++ b/src/compositor_x11.h @@ -0,0 +1,112 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2011 Arthur Arlt + SPDX-FileCopyrightText: 2012 Martin Gräßlin + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "compositor.h" + +namespace KWin +{ + +class X11SyncManager; +class X11Window; + +class KWIN_EXPORT X11Compositor final : public Compositor +{ + Q_OBJECT +public: + enum SuspendReason { + NoReasonSuspend = 0, + UserSuspend = 1 << 0, + BlockRuleSuspend = 1 << 1, + ScriptSuspend = 1 << 2, + AllReasonSuspend = 0xff + }; + Q_DECLARE_FLAGS(SuspendReasons, SuspendReason) + Q_ENUM(SuspendReason) + Q_FLAG(SuspendReasons) + + static X11Compositor *create(QObject *parent = nullptr); + ~X11Compositor() override; + + X11SyncManager *syncManager() const; + + /** + * @brief Suspends the Compositor if it is currently active. + * + * Note: it is possible that the Compositor is not able to suspend. Use isActive to check + * whether the Compositor has been suspended. + * + * @return void + * @see resume + * @see isActive + */ + void suspend(SuspendReason reason); + + /** + * @brief Resumes the Compositor if it is currently suspended. + * + * Note: it is possible that the Compositor cannot be resumed, that is there might be Clients + * blocking the usage of Compositing or the Scene might be broken. Use isActive to check + * whether the Compositor has been resumed. Also check isCompositingPossible and + * isOpenGLBroken. + * + * Note: The starting of the Compositor can require some time and is partially done threaded. + * After this method returns the setup may not have been completed. + * + * @return void + * @see suspend + * @see isActive + * @see isCompositingPossible + * @see isOpenGLBroken + */ + void resume(SuspendReason reason); + + void toggleCompositing() override; + void reinitialize() override; + void configChanged() override; + bool compositingPossible() const override; + QString compositingNotPossibleReason() const override; + bool openGLCompositingIsBroken() const override; + void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; + + /** + * Checks whether @p w is the Scene's overlay window. + */ + bool checkForOverlayWindow(WId w) const; + + /** + * @returns Whether the Scene's Overlay X Window is visible. + */ + bool isOverlayWindowVisible() const; + + void updateClientCompositeBlocking(X11Window *client = nullptr); + + static X11Compositor *self(); + +protected: + void start() override; + void stop() override; + void composite(RenderLoop *renderLoop) override; + +private: + explicit X11Compositor(QObject *parent); + + std::unique_ptr m_openGLFreezeProtectionThread; + std::unique_ptr m_openGLFreezeProtection; + std::unique_ptr m_syncManager; + /** + * Whether the Compositor is currently suspended, 8 bits encoding the reason + */ + SuspendReasons m_suspended; + int m_framesToTestForSafety = 3; +}; + +} // namespace KWin diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp index 13fe555787..bec8593cec 100644 --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -15,7 +15,7 @@ // kwin #include "atoms.h" -#include "compositor.h" +#include "compositor_x11.h" #include "core/output.h" #include "core/renderbackend.h" #include "debug_console.h" diff --git a/src/main_wayland.cpp b/src/main_wayland.cpp index 029acb2d26..4c9f82c6ba 100644 --- a/src/main_wayland.cpp +++ b/src/main_wayland.cpp @@ -14,7 +14,7 @@ #include "backends/virtual/virtual_backend.h" #include "backends/wayland/wayland_backend.h" #include "backends/x11/windowed/x11_windowed_backend.h" -#include "compositor.h" +#include "compositor_wayland.h" #include "core/outputbackend.h" #include "core/session.h" #include "effects.h" diff --git a/src/scene/surfaceitem_x11.cpp b/src/scene/surfaceitem_x11.cpp index 91803c9f25..2857c62235 100644 --- a/src/scene/surfaceitem_x11.cpp +++ b/src/scene/surfaceitem_x11.cpp @@ -5,7 +5,7 @@ */ #include "scene/surfaceitem_x11.h" -#include "compositor.h" +#include "compositor_x11.h" #include "core/renderbackend.h" #include "x11syncmanager.h" #include "x11window.h" diff --git a/src/workspace.cpp b/src/workspace.cpp index 1cbd7a366f..c7b23e4c5b 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -20,7 +20,7 @@ #endif #include "appmenu.h" #include "atoms.h" -#include "compositor.h" +#include "compositor_x11.h" #include "core/outputbackend.h" #include "core/outputconfiguration.h" #include "cursor.h"