diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -407,6 +407,7 @@ appmenu.cpp atoms.cpp client_machine.cpp + compositingdisablednotifier.cpp colorcorrection/clockskewnotifier.cpp colorcorrection/clockskewnotifierengine.cpp colorcorrection/colorcorrectdbusinterface.cpp diff --git a/composite.h b/composite.h --- a/composite.h +++ b/composite.h @@ -30,6 +30,7 @@ namespace KWin { +class CompositingDisabledNotifier; class CompositorSelectionOwner; class Scene; class X11Client; @@ -269,6 +270,8 @@ */ SuspendReasons m_suspended; + CompositingDisabledNotifier *m_disabledNotifier = nullptr; + int m_xrrRefreshRate; }; diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -21,6 +21,7 @@ #include "dbusinterface.h" #include "x11client.h" +#include "compositingdisablednotifier.h" #include "decorations/decoratedclient.h" #include "deleted.h" #include "effects.h" @@ -945,6 +946,14 @@ void X11Compositor::start() { if (m_suspended) { + if (kwinApp()->platform()->compositingDisabledAfterCrash()) { + qCWarning(KWIN_CORE) << "Compositing is disabled because KWin crashed several times in a row"; + if (!m_disabledNotifier) { + m_disabledNotifier = new CompositingDisabledNotifier(this); + } + return; + } + QStringList reasons; if (m_suspended & UserSuspend) { reasons << QStringLiteral("Disabled by User"); @@ -965,6 +974,10 @@ // Internal setup failed, abort. return; } + + delete m_disabledNotifier; + m_disabledNotifier = nullptr; + m_xrrRefreshRate = KWin::currentRefreshRate(); startupWithWorkspace(); } diff --git a/compositingdisablednotifier.h b/compositingdisablednotifier.h new file mode 100644 --- /dev/null +++ b/compositingdisablednotifier.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018 David Edmundson + * Copyright 2020 Kai Uwe Broulik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +namespace KWin +{ + +/** + * Responsible for showing an SNI if compositing was disabled + * due to crashes or driver problems and allows the user to + * open the Compositing KCM + */ + +class CompositingDisabledNotifier : public KStatusNotifierItem +{ + Q_OBJECT +public: + CompositingDisabledNotifier(QObject *parent = nullptr); + ~CompositingDisabledNotifier() override; +}; + +} // namespace KWin diff --git a/compositingdisablednotifier.cpp b/compositingdisablednotifier.cpp new file mode 100644 --- /dev/null +++ b/compositingdisablednotifier.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2018 David Edmundson + * Copyright 2020 Kai Uwe Broulik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "compositingdisablednotifier.h" + +#include +#include +#include + +using namespace KWin; + +CompositingDisabledNotifier::CompositingDisabledNotifier(QObject *parent) + : KStatusNotifierItem(parent) +{ + setTitle(i18n("Compositing Disabled")); + setToolTipTitle(i18nc("@info:tooltip", "Compositing is Disabled")); + setToolTipSubTitle(i18nc("Tooltip telling user KWin compositing is disabled", "Visual appearance may be degraded")); + setIconByName(QStringLiteral("video-card-inactive")); + setStatus(KStatusNotifierItem::Active); + setStandardActionsEnabled(false); // Don't let SNI quit KWin + + connect(this, &KStatusNotifierItem::activateRequested, this, []() { + QProcess::startDetached("kcmshell5 kwincompositing"); + }); +} + +CompositingDisabledNotifier::~CompositingDisabledNotifier() = default; + diff --git a/kcmkwin/kwincompositing/compositing.h b/kcmkwin/kwincompositing/compositing.h --- a/kcmkwin/kwincompositing/compositing.h +++ b/kcmkwin/kwincompositing/compositing.h @@ -51,8 +51,10 @@ public: explicit Compositing(QObject *parent = nullptr); + Q_INVOKABLE bool compositingDisabledAfterCrash() const; Q_INVOKABLE bool OpenGLIsUnsafe() const; Q_INVOKABLE bool OpenGLIsBroken(); + Q_INVOKABLE void resetCompositingDisabledAfterCrash(); Q_INVOKABLE void reenableOpenGLDetection(); qreal animationSpeed() const; int windowThumbnail() const; diff --git a/kcmkwin/kwincompositing/compositing.cpp b/kcmkwin/kwincompositing/compositing.cpp --- a/kcmkwin/kwincompositing/compositing.cpp +++ b/kcmkwin/kwincompositing/compositing.cpp @@ -134,6 +134,12 @@ m_changed = true; } +bool Compositing::compositingDisabledAfterCrash() const +{ + KConfigGroup kwinConfig(m_config, "Compositing"); + return kwinConfig.readEntry("CompositingDisabledAfterCrash", false); +} + bool Compositing::OpenGLIsUnsafe() const { KConfigGroup kwinConfig(m_config, "Compositing"); @@ -159,6 +165,13 @@ return false; } +void Compositing::resetCompositingDisabledAfterCrash() +{ + KConfigGroup kwinConfig(m_config, "Compositing"); + kwinConfig.writeEntry("CompositingDisabledAfterCrash", false); + kwinConfig.sync(); +} + void Compositing::reenableOpenGLDetection() { KConfigGroup kwinConfig(m_config, "Compositing"); diff --git a/kcmkwin/kwincompositing/compositing.ui b/kcmkwin/kwincompositing/compositing.ui --- a/kcmkwin/kwincompositing/compositing.ui +++ b/kcmkwin/kwincompositing/compositing.ui @@ -33,6 +33,20 @@ + + + + false + + + Compositing was disabled after KWin crashed several times in a row. +If you think this was a singular instance, you can reset this protection. + + + true + + + diff --git a/kcmkwin/kwincompositing/main.cpp b/kcmkwin/kwincompositing/main.cpp --- a/kcmkwin/kwincompositing/main.cpp +++ b/kcmkwin/kwincompositing/main.cpp @@ -61,6 +61,17 @@ connect(reenableGLAction, &QAction::triggered, m_compositing, &KWin::Compositing::Compositing::reenableOpenGLDetection); connect(reenableGLAction, &QAction::triggered, m_form.glCrashedWarning, &KMessageWidget::animatedHide); m_form.glCrashedWarning->addAction(reenableGLAction); + + m_form.compositingDisabledAfterCrashWarning->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning"))); + QAction *resetCompositingDisabledAfterCrashAction = new QAction(i18n("Re-enable Compositing")); + connect(resetCompositingDisabledAfterCrashAction, &QAction::triggered, this, [this] { + m_compositing->resetCompositingDisabledAfterCrash(); + m_compositing->setCompositingEnabled(true); + }); + connect(resetCompositingDisabledAfterCrashAction, &QAction::triggered, m_form.compositingDisabledAfterCrashWarning, &KMessageWidget::animatedHide); + m_form.compositingDisabledAfterCrashWarning->addAction(resetCompositingDisabledAfterCrashAction); + + m_form.compositingDisabledAfterCrashWarning->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning"))); m_form.scaleWarning->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning"))); m_form.tearingWarning->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning"))); m_form.windowThumbnailWarning->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning"))); @@ -200,6 +211,10 @@ if (m_compositing->OpenGLIsUnsafe()) { m_form.glCrashedWarning->animatedShow(); } + + if (m_compositing->compositingDisabledAfterCrash()) { + m_form.compositingDisabledAfterCrashWarning->animatedShow(); + } } void KWinCompositingSettings::load() diff --git a/main_x11.cpp b/main_x11.cpp --- a/main_x11.cpp +++ b/main_x11.cpp @@ -308,7 +308,10 @@ // Disable compositing if we have had too many crashes qCDebug(KWIN_CORE) << "Too many crashes recently, disabling compositing"; KConfigGroup compgroup(KSharedConfig::openConfig(), "Compositing"); - compgroup.writeEntry("Enabled", false); + if (compgroup.readEntry("Enabled", true)) { + compgroup.writeEntry("Enabled", false); + compgroup.writeEntry("CompositingDisabledAfterCrash", true); + } } // Reset crashes count if we stay up for more that 15 seconds QTimer::singleShot(15 * 1000, this, SLOT(resetCrashesCount())); diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -217,6 +217,12 @@ * @see createOpenGLSafePoint */ virtual bool openGLCompositingIsBroken() const; + /** + * Whether compositing is disabled after KWin crashed several times in a row. + * + * Default implementation returns @c false. + **/ + virtual bool compositingDisabledAfterCrash() const; enum class OpenGLSafePoint { PreInit, PostInit, diff --git a/platform.cpp b/platform.cpp --- a/platform.cpp +++ b/platform.cpp @@ -479,6 +479,11 @@ return false; } +bool Platform::compositingDisabledAfterCrash() const +{ + return false; +} + void Platform::createOpenGLSafePoint(OpenGLSafePoint safePoint) { Q_UNUSED(safePoint) diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -53,6 +53,7 @@ bool compositingPossible() const override; QString compositingNotPossibleReason() const override; bool openGLCompositingIsBroken() const override; + bool compositingDisabledAfterCrash() const override; void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()) override; void startInteractivePositionSelection(std::function callback) override; diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -167,19 +167,33 @@ return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false); } +bool X11StandalonePlatform::compositingDisabledAfterCrash() const +{ + const QString unsafeKey(QLatin1String("CompositingDisabledAfterCrash") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); + return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false); +} + QString X11StandalonePlatform::compositingNotPossibleReason() const { + const QString multiheadScreen = (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()); + // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && - gl_workaround_group.readEntry(unsafeKey, false)) + KConfigGroup compositingGroup(kwinApp()->config(), "Compositing"); + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + multiheadScreen); + if (compositingGroup.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && + compositingGroup.readEntry(unsafeKey, false)) 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!

" "

Alternatively, you might want to use the XRender backend instead.

"); + const QString disabledAfterCrashKey(QLatin1String("CompositingDisabledAfterCrash") + multiheadScreen); + if (compositingGroup.readEntry(disabledAfterCrashKey, false)) { + return i18n("Compositing was disabled after KWin crashed several times in a row.
" + "

If you think this was a singular instance, you can reset this protection.

"); + } + if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { return i18n("Required X extensions (XComposite and XDamage) are not available."); } @@ -197,13 +211,19 @@ bool X11StandalonePlatform::compositingPossible() const { + const QString multiheadScreen = (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()); + // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && - gl_workaround_group.readEntry(unsafeKey, false)) + KConfigGroup compositingGroup(kwinApp()->config(), "Compositing"); + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + multiheadScreen); + if (compositingGroup.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && + compositingGroup.readEntry(unsafeKey, false)) return false; + const QString disabledAfterCrashKey(QLatin1String("CompositingDisabledAfterCrash") + multiheadScreen); + if (compositingGroup.readEntry(disabledAfterCrashKey, false)) { + return false; + } if (!Xcb::Extensions::self()->isCompositeAvailable()) { qCDebug(KWIN_CORE) << "No composite extension available";