Desaturate non-responsive windows
When an application is not responding, its window is desaturated to communicate this. Also "(Not Responding)" is added to the title bar. Differential Revision: https://phabricator.kde.org/D5245
This commit is contained in:
parent
5d044331fc
commit
1eb950a985
12 changed files with 259 additions and 6 deletions
|
@ -1735,4 +1735,18 @@ void AbstractClient::showApplicationMenu(int actionId)
|
|||
}
|
||||
}
|
||||
|
||||
bool AbstractClient::unresponsive() const
|
||||
{
|
||||
return m_unresponsive;
|
||||
}
|
||||
|
||||
void AbstractClient::setUnresponsive(bool unresponsive)
|
||||
{
|
||||
if (m_unresponsive != unresponsive) {
|
||||
m_unresponsive = unresponsive;
|
||||
emit unresponsiveChanged(m_unresponsive);
|
||||
emit captionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -265,6 +265,14 @@ class KWIN_EXPORT AbstractClient : public Toplevel
|
|||
*/
|
||||
Q_PROPERTY(bool applicationMenuActive READ applicationMenuActive NOTIFY applicationMenuActiveChanged)
|
||||
|
||||
/**
|
||||
* Whether this client is unresponsive.
|
||||
*
|
||||
* When an application failed to react on a ping request in time, it is
|
||||
* considered unresponsive. This usually indicates that the application froze or crashed.
|
||||
*/
|
||||
Q_PROPERTY(bool unresponsive READ unresponsive NOTIFY unresponsiveChanged)
|
||||
|
||||
public:
|
||||
virtual ~AbstractClient();
|
||||
|
||||
|
@ -653,6 +661,8 @@ public:
|
|||
*/
|
||||
void showApplicationMenu(int actionId);
|
||||
|
||||
bool unresponsive() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void closeWindow() = 0;
|
||||
|
||||
|
@ -694,6 +704,7 @@ Q_SIGNALS:
|
|||
void desktopFileNameChanged();
|
||||
void hasApplicationMenuChanged(bool);
|
||||
void applicationMenuActiveChanged(bool);
|
||||
void unresponsiveChanged(bool);
|
||||
|
||||
protected:
|
||||
AbstractClient();
|
||||
|
@ -978,6 +989,8 @@ protected:
|
|||
void updateApplicationMenuServiceName(const QString &serviceName);
|
||||
void updateApplicationMenuObjectPath(const QString &objectPath);
|
||||
|
||||
void setUnresponsive(bool unresponsive);
|
||||
|
||||
private:
|
||||
void handlePaletteChange();
|
||||
QSharedPointer<TabBox::TabBoxClientImpl> m_tabBoxClient;
|
||||
|
@ -1050,6 +1063,8 @@ private:
|
|||
QString m_applicationMenuServiceName;
|
||||
QString m_applicationMenuObjectPath;
|
||||
|
||||
bool m_unresponsive = false;
|
||||
|
||||
static bool s_haveResizeEffect;
|
||||
};
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ void TestScriptedEffectLoader::testHasEffect_data()
|
|||
QTest::newRow("Fade + kwin4_effect") << QStringLiteral("kwin4_effect_fade") << true;
|
||||
QTest::newRow("Fade + kwin4_effect + CS") << QStringLiteral("kwin4_eFfect_fAde") << true;
|
||||
QTest::newRow("FadeDesktop") << QStringLiteral("kwin4_effect_fadedesktop") << true;
|
||||
QTest::newRow("FrozenApp") << QStringLiteral("kwin4_effect_frozenapp") << true;
|
||||
QTest::newRow("DialogParent") << QStringLiteral("kwin4_effect_dialogparent") << true;
|
||||
QTest::newRow("Login") << QStringLiteral("kwin4_effect_login") << true;
|
||||
QTest::newRow("Logout") << QStringLiteral("kwin4_effect_logout") << true;
|
||||
|
@ -165,6 +166,7 @@ void TestScriptedEffectLoader::testKnownEffects()
|
|||
expectedEffects << QStringLiteral("kwin4_effect_dialogparent")
|
||||
<< QStringLiteral("kwin4_effect_fade")
|
||||
<< QStringLiteral("kwin4_effect_fadedesktop")
|
||||
<< QStringLiteral("kwin4_effect_frozenapp")
|
||||
<< QStringLiteral("kwin4_effect_login")
|
||||
<< QStringLiteral("kwin4_effect_logout")
|
||||
<< QStringLiteral("kwin4_effect_maximize")
|
||||
|
@ -190,6 +192,7 @@ void TestScriptedEffectLoader::testLoadEffect_data()
|
|||
QTest::newRow("Fade + kwin4_effect") << QStringLiteral("kwin4_effect_fade") << true;
|
||||
QTest::newRow("Fade + kwin4_effect + CS") << QStringLiteral("kwin4_eFfect_fAde") << true;
|
||||
QTest::newRow("FadeDesktop") << QStringLiteral("kwin4_effect_fadedesktop") << true;
|
||||
QTest::newRow("FrozenApp") << QStringLiteral("kwin4_effect_frozenapp") << true;
|
||||
QTest::newRow("DialogParent") << QStringLiteral("kwin4_effect_dialogparent") << true;
|
||||
QTest::newRow("Login") << QStringLiteral("kwin4_effect_login") << true;
|
||||
QTest::newRow("Logout") << QStringLiteral("kwin4_effect_logout") << true;
|
||||
|
@ -342,6 +345,7 @@ void TestScriptedEffectLoader::testLoadAllEffects()
|
|||
plugins.writeEntry(kwin4 + QStringLiteral("dialogparentEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("fadeEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("fadedesktopEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("frozenappEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("loginEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("logoutEnabled"), false);
|
||||
plugins.writeEntry(kwin4 + QStringLiteral("maximizeEnabled"), false);
|
||||
|
|
31
client.cpp
31
client.cpp
|
@ -39,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <KDecoration2/Decoration>
|
||||
#include <KDecoration2/DecoratedClient>
|
||||
// KDE
|
||||
#include <KLocalizedString>
|
||||
#include <KWindowSystem>
|
||||
#include <KColorScheme>
|
||||
// Qt
|
||||
|
@ -1124,14 +1125,24 @@ void Client::pingWindow()
|
|||
ping_timer = new QTimer(this);
|
||||
connect(ping_timer, &QTimer::timeout, this,
|
||||
[this]() {
|
||||
qCDebug(KWIN_CORE) << "Ping timeout:" << caption();
|
||||
ping_timer->deleteLater();
|
||||
ping_timer = nullptr;
|
||||
killProcess(true, m_pingTimestamp);
|
||||
if (unresponsive()) {
|
||||
qCDebug(KWIN_CORE) << "Final ping timeout, asking to kill:" << caption();
|
||||
ping_timer->deleteLater();
|
||||
ping_timer = nullptr;
|
||||
killProcess(true, m_pingTimestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(KWIN_CORE) << "First ping timeout:" << caption();
|
||||
|
||||
setUnresponsive(true);
|
||||
ping_timer->start();
|
||||
}
|
||||
);
|
||||
ping_timer->setSingleShot(true);
|
||||
ping_timer->start(options->killPingTimeout());
|
||||
// we'll run the timer twice, at first we'll desaturate the window
|
||||
// and the second time we'll show the "do you want to kill" prompt
|
||||
ping_timer->start(options->killPingTimeout() / 2);
|
||||
m_pingTimestamp = xTime();
|
||||
workspace()->sendPingToWindow(window(), m_pingTimestamp);
|
||||
}
|
||||
|
@ -1143,6 +1154,9 @@ void Client::gotPing(xcb_timestamp_t timestamp)
|
|||
return;
|
||||
delete ping_timer;
|
||||
ping_timer = NULL;
|
||||
|
||||
setUnresponsive(false);
|
||||
|
||||
if (m_killHelperPID && !::kill(m_killHelperPID, 0)) { // means the process is alive
|
||||
::kill(m_killHelperPID, SIGTERM);
|
||||
m_killHelperPID = 0;
|
||||
|
@ -1536,8 +1550,13 @@ void Client::fetchIconicName()
|
|||
QString Client::caption(bool full, bool stripped) const
|
||||
{
|
||||
QString cap = stripped ? cap_deco : cap_normal;
|
||||
if (full)
|
||||
if (full) {
|
||||
cap += cap_suffix;
|
||||
if (unresponsive()) {
|
||||
cap += QLatin1String(" ");
|
||||
cap += i18nc("Application is not responding, appended to window title", "(Not Responding)");
|
||||
}
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
|
|
@ -284,6 +284,11 @@ void EffectsHandlerImpl::setupAbstractClientConnections(AbstractClient* c)
|
|||
connect(c, &AbstractClient::modalChanged, this, &EffectsHandlerImpl::slotClientModalityChanged);
|
||||
connect(c, &AbstractClient::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
|
||||
connect(c, &AbstractClient::damaged, this, &EffectsHandlerImpl::slotWindowDamaged);
|
||||
connect(c, &AbstractClient::unresponsiveChanged, this,
|
||||
[this, c](bool unresponsive) {
|
||||
emit windowUnresponsiveChanged(c->effectWindow(), unresponsive);
|
||||
}
|
||||
);
|
||||
connect(c, &AbstractClient::windowShown, this,
|
||||
[this](Toplevel *c) {
|
||||
emit windowShown(c->effectWindow());
|
||||
|
|
|
@ -132,6 +132,7 @@ add_subdirectory( dialogparent )
|
|||
add_subdirectory( eyeonscreen )
|
||||
add_subdirectory( fade )
|
||||
add_subdirectory( fadedesktop )
|
||||
add_subdirectory( frozenapp )
|
||||
add_subdirectory( login )
|
||||
add_subdirectory( logout )
|
||||
add_subdirectory( maximize )
|
||||
|
|
1
effects/frozenapp/CMakeLists.txt
Normal file
1
effects/frozenapp/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
|||
add_subdirectory(package)
|
6
effects/frozenapp/package/CMakeLists.txt
Normal file
6
effects/frozenapp/package/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
install(DIRECTORY contents DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/effects/kwin4_effect_frozenapp)
|
||||
install(FILES metadata.desktop DESTINATION ${DATA_INSTALL_DIR}/${KWIN_NAME}/effects/kwin4_effect_frozenapp)
|
||||
|
||||
install(FILES metadata.desktop
|
||||
DESTINATION ${SERVICES_INSTALL_DIR}/${KWIN_NAME}
|
||||
RENAME kwin4_effect_frozenapp.desktop)
|
142
effects/frozenapp/package/contents/code/main.js
Normal file
142
effects/frozenapp/package/contents/code/main.js
Normal file
|
@ -0,0 +1,142 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2017 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
/*global effect, effects, animate, animationTime, Effect*/
|
||||
var frozenAppEffect = {
|
||||
inDuration: animationTime(1500),
|
||||
outDuration: animationTime(250),
|
||||
loadConfig: function () {
|
||||
"use strict";
|
||||
frozenAppEffect.inDuration = animationTime(1500);
|
||||
frozenAppEffect.outDuration = animationTime(250);
|
||||
},
|
||||
windowAdded: function (window) {
|
||||
"use strict";
|
||||
if (!window || !window.unresponsive) {
|
||||
return;
|
||||
}
|
||||
frozenAppEffect.windowBecameUnresponsive(window);
|
||||
},
|
||||
windowBecameUnresponsive: function (window) {
|
||||
"use strict";
|
||||
if (window.unresponsiveAnimation) {
|
||||
return;
|
||||
}
|
||||
frozenAppEffect.startAnimation(window, frozenAppEffect.inDuration);
|
||||
},
|
||||
startAnimation: function (window, duration) {
|
||||
"use strict";
|
||||
if (!window.visible) {
|
||||
return;
|
||||
}
|
||||
window.unresponsiveAnimation = set({
|
||||
window: window,
|
||||
duration: duration,
|
||||
animations: [{
|
||||
type: Effect.Saturation,
|
||||
to: 0.1
|
||||
}, {
|
||||
type: Effect.Brightness,
|
||||
to: 1.5
|
||||
}]
|
||||
});
|
||||
},
|
||||
windowClosed: function (window) {
|
||||
"use strict";
|
||||
frozenAppEffect.cancelAnimation(window);
|
||||
if (!window.unresponsive) {
|
||||
return;
|
||||
}
|
||||
frozenAppEffect.windowBecameResponsive(window);
|
||||
},
|
||||
windowBecameResponsive: function (window) {
|
||||
"use strict";
|
||||
if (!window.unresponsiveAnimation) {
|
||||
return;
|
||||
}
|
||||
cancel(window.unresponsiveAnimation);
|
||||
window.unresponsiveAnimation = undefined;
|
||||
|
||||
animate({
|
||||
window: window,
|
||||
duration: frozenAppEffect.outDuration,
|
||||
animations: [{
|
||||
type: Effect.Saturation,
|
||||
from: 0.1,
|
||||
to: 1.0
|
||||
}, {
|
||||
type: Effect.Brightness,
|
||||
from: 1.5,
|
||||
to: 1.0
|
||||
}]
|
||||
});
|
||||
},
|
||||
cancelAnimation: function (window) {
|
||||
"use strict";
|
||||
if (window.unresponsiveAnimation) {
|
||||
print(window);
|
||||
cancel(window.unresponsiveAnimation);
|
||||
window.unresponsiveAnimation = undefined;
|
||||
}
|
||||
},
|
||||
desktopChanged: function () {
|
||||
"use strict";
|
||||
|
||||
var windows = effects.stackingOrder;
|
||||
for (var i = 0, length = windows.length; i < length; ++i) {
|
||||
print(i);
|
||||
var window = windows[i];
|
||||
frozenAppEffect.cancelAnimation(window);
|
||||
frozenAppEffect.restartAnimation(window);
|
||||
}
|
||||
},
|
||||
unresponsiveChanged: function (window) {
|
||||
"use strict";
|
||||
if (window.unresponsive) {
|
||||
frozenAppEffect.windowBecameUnresponsive(window);
|
||||
} else {
|
||||
frozenAppEffect.windowBecameResponsive(window);
|
||||
}
|
||||
},
|
||||
restartAnimation: function (window) {
|
||||
"use strict";
|
||||
if (!window || !window.unresponsive) {
|
||||
return;
|
||||
}
|
||||
frozenAppEffect.startAnimation(window, 1);
|
||||
},
|
||||
init: function () {
|
||||
"use strict";
|
||||
|
||||
effects.windowAdded.connect(frozenAppEffect.windowAdded);
|
||||
effects.windowClosed.connect(frozenAppEffect.windowClosed);
|
||||
effects.windowMinimized.connect(frozenAppEffect.cancelAnimation);
|
||||
effects.windowUnminimized.connect(frozenAppEffect.restartAnimation);
|
||||
effects.windowUnresponsiveChanged.connect(frozenAppEffect.unresponsiveChanged);
|
||||
effects['desktopChanged(int,int)'].connect(frozenAppEffect.desktopChanged);
|
||||
effects.desktopPresenceChanged.connect(frozenAppEffect.cancelAnimation);
|
||||
effects.desktopPresenceChanged.connect(frozenAppEffect.restartAnimation);
|
||||
|
||||
var windows = effects.stackingOrder;
|
||||
for (var i = 0, length = windows.length; i < length; ++i) {
|
||||
frozenAppEffect.restartAnimation(windows[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
frozenAppEffect.init();
|
22
effects/frozenapp/package/metadata.desktop
Normal file
22
effects/frozenapp/package/metadata.desktop
Normal file
|
@ -0,0 +1,22 @@
|
|||
[Desktop Entry]
|
||||
Name=Desaturate Unresponsive Applications
|
||||
Icon=preferences-system-windows-effect-frozenapp
|
||||
Comment=Desaturate windows of unresponsive (frozen) applications
|
||||
|
||||
Type=Service
|
||||
X-KDE-ServiceTypes=KWin/Effect,KCModule
|
||||
X-KDE-PluginInfo-Author=Kai Uwe Broulik
|
||||
X-KDE-PluginInfo-Email=kde@privat.broulik.de
|
||||
X-KDE-PluginInfo-Name=kwin4_effect_frozenapp
|
||||
X-KDE-PluginInfo-Version=1.0
|
||||
X-KDE-PluginInfo-Category=Appearance
|
||||
X-KDE-PluginInfo-Depends=
|
||||
X-KDE-PluginInfo-License=GPL
|
||||
X-KDE-PluginInfo-EnabledByDefault=true
|
||||
X-KDE-Ordering=60
|
||||
X-Plasma-API=javascript
|
||||
X-Plasma-MainScript=code/main.js
|
||||
X-KDE-PluginKeyword=kwin4_effect_frozenapp
|
||||
X-KDE-Library=kcm_kwin4_genericscripted
|
||||
X-KDE-ParentComponents=kwin4_effect_frozen
|
||||
X-KWin-Config-TranslationDomain=kwin_effects
|
|
@ -873,6 +873,7 @@ WINDOW_HELPER_DEFAULT(bool, isSkipSwitcher, "skipSwitcher", false)
|
|||
WINDOW_HELPER_DEFAULT(bool, isCurrentTab, "isCurrentTab", true)
|
||||
WINDOW_HELPER_DEFAULT(bool, decorationHasAlpha, "decorationHasAlpha", false)
|
||||
WINDOW_HELPER_DEFAULT(bool, isFullScreen, "fullScreen", false)
|
||||
WINDOW_HELPER_DEFAULT(bool, isUnresponsive, "unresponsive", false)
|
||||
|
||||
#undef WINDOW_HELPER_DEFAULT
|
||||
|
||||
|
|
|
@ -1442,6 +1442,14 @@ Q_SIGNALS:
|
|||
* @since 4.11
|
||||
**/
|
||||
void windowModalityChanged(KWin::EffectWindow *w);
|
||||
/**
|
||||
* Signal emitted when a window either became unresponsive (eg. app froze or crashed)
|
||||
* or respoonsive
|
||||
* @param w The window that became (un)responsive
|
||||
* @param unresponsive Whether the window is responsive or unresponsive
|
||||
* @since 5.10
|
||||
*/
|
||||
void windowUnresponsiveChanged(KWin::EffectWindow *w, bool unresponsive);
|
||||
/**
|
||||
* Signal emitted when an area of a window is scheduled for repainting.
|
||||
* Use this signal in an effect if another area needs to be synced as well.
|
||||
|
@ -1867,6 +1875,16 @@ class KWINEFFECTS_EXPORT EffectWindow : public QObject
|
|||
* @since 5.6
|
||||
**/
|
||||
Q_PROPERTY(bool fullScreen READ isFullScreen)
|
||||
|
||||
/**
|
||||
* Whether this client is unresponsive.
|
||||
*
|
||||
* When an application failed to react on a ping request in time, it is
|
||||
* considered unresponsive. This usually indicates that the application froze or crashed.
|
||||
*
|
||||
* @since 5.10
|
||||
*/
|
||||
Q_PROPERTY(bool unresponsive READ isUnresponsive)
|
||||
public:
|
||||
/** Flags explaining why painting should be disabled */
|
||||
enum {
|
||||
|
@ -2109,6 +2127,11 @@ public:
|
|||
**/
|
||||
bool isFullScreen() const;
|
||||
|
||||
/**
|
||||
* @since 5.10
|
||||
*/
|
||||
bool isUnresponsive() const;
|
||||
|
||||
/**
|
||||
* Can be used to by effects to store arbitrary data in the EffectWindow.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue