[nightcolor] Introduce inhibition API

Summary:
The new API allows to block Night Color temporarily. This can be useful
for applications such as video games and plasma applets.

CCBUG: 400418

Test Plan: Called inhibit() and uninhibit() from D-Feet.

Reviewers: #kwin, davidedmundson

Reviewed By: #kwin, davidedmundson

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D25786
This commit is contained in:
Vlad Zahorodnii 2020-01-07 17:09:55 +02:00
parent 216c005e28
commit 5bec89ac90
5 changed files with 211 additions and 36 deletions

View file

@ -23,18 +23,51 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "manager.h"
#include <QDBusMessage>
namespace KWin {
namespace ColorCorrect {
ColorCorrectDBusInterface::ColorCorrectDBusInterface(Manager *parent)
: QObject(parent)
, m_manager(parent)
, m_inhibitorWatcher(new QDBusServiceWatcher(this))
{
m_inhibitorWatcher->setConnection(QDBusConnection::sessionBus());
m_inhibitorWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
connect(m_inhibitorWatcher, &QDBusServiceWatcher::serviceUnregistered,
this, &ColorCorrectDBusInterface::removeInhibitorService);
// Argh, all this code is just to send one innocent signal...
connect(m_manager, &Manager::inhibitedChanged, this, [this] {
QVariantMap changedProperties;
changedProperties.insert(QStringLiteral("inhibited"), m_manager->isInhibited());
QDBusMessage message = QDBusMessage::createSignal(
QStringLiteral("/ColorCorrect"),
QStringLiteral("org.freedesktop.DBus.Properties"),
QStringLiteral("PropertiesChanged")
);
message.setArguments({
QStringLiteral("org.kde.kwin.ColorCorrect"),
changedProperties,
QStringList(), // invalidated_properties
});
QDBusConnection::sessionBus().send(message);
});
connect(m_manager, &Manager::configChange, this, &ColorCorrectDBusInterface::nightColorConfigChanged);
new ColorCorrectAdaptor(this);
QDBusConnection::sessionBus().registerObject(QStringLiteral("/ColorCorrect"), this);
}
bool ColorCorrectDBusInterface::isInhibited() const
{
return m_manager->isInhibited();
}
QHash<QString, QVariant> ColorCorrectDBusInterface::nightColorInfo()
{
return m_manager->info();
@ -50,5 +83,49 @@ void ColorCorrectDBusInterface::nightColorAutoLocationUpdate(double latitude, do
m_manager->autoLocationUpdate(latitude, longitude);
}
uint ColorCorrectDBusInterface::inhibit()
{
const QString serviceName = QDBusContext::message().service();
if (m_inhibitors.values(serviceName).isEmpty()) {
m_inhibitorWatcher->addWatchedService(serviceName);
}
m_inhibitors.insert(serviceName, ++m_lastInhibitionCookie);
m_manager->inhibit();
return m_lastInhibitionCookie;
}
void ColorCorrectDBusInterface::uninhibit(uint cookie)
{
const QString serviceName = QDBusContext::message().service();
uninhibit(serviceName, cookie);
}
void ColorCorrectDBusInterface::uninhibit(const QString &serviceName, uint cookie)
{
const int removedCount = m_inhibitors.remove(serviceName, cookie);
if (!removedCount) {
return;
}
if (m_inhibitors.values(serviceName).isEmpty()) {
m_inhibitorWatcher->removeWatchedService(serviceName);
}
m_manager->uninhibit();
}
void ColorCorrectDBusInterface::removeInhibitorService(const QString &serviceName)
{
const auto cookies = m_inhibitors.values(serviceName);
for (const uint &cookie : cookies) {
uninhibit(serviceName, cookie);
}
}
}
}

View file

@ -32,15 +32,18 @@ namespace ColorCorrect
class Manager;
class ColorCorrectDBusInterface : public QObject
class ColorCorrectDBusInterface : public QObject, public QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.ColorCorrect")
Q_PROPERTY(bool inhibited READ isInhibited)
public:
explicit ColorCorrectDBusInterface(Manager *parent);
~ColorCorrectDBusInterface() override = default;
bool isInhibited() const;
public Q_SLOTS:
/**
* @brief Gives information about the current state of Night Color.
@ -101,6 +104,16 @@ public Q_SLOTS:
* @since 5.12
*/
void nightColorAutoLocationUpdate(double latitude, double longitude);
/**
* @brief Temporarily blocks Night Color.
* @since 5.18
*/
uint inhibit();
/**
* @brief Cancels the previous call to inhibit().
* @since 5.18
*/
void uninhibit(uint cookie);
Q_SIGNALS:
/**
@ -115,8 +128,16 @@ Q_SIGNALS:
*/
void nightColorConfigChanged(QHash<QString, QVariant> data);
private Q_SLOTS:
void removeInhibitorService(const QString &serviceName);
private:
void uninhibit(const QString &serviceName, uint cookie);
Manager *m_manager;
QDBusServiceWatcher *m_inhibitorWatcher;
QMultiHash<QString, uint> m_inhibitors;
uint m_lastInhibitionCookie = 0;
};
}

View file

@ -61,6 +61,27 @@ Manager::Manager(QObject *parent)
{
m_iface = new ColorCorrectDBusInterface(this);
connect(kwinApp(), &Application::workspaceCreated, this, &Manager::init);
// Display a message when Night Color is (un)inhibited.
connect(this, &Manager::inhibitedChanged, this, [this] {
// TODO: Maybe use different icons?
const QString iconName = isInhibited()
? QStringLiteral("preferences-desktop-display-nightcolor-off")
: QStringLiteral("preferences-desktop-display-nightcolor-on");
const QString text = isInhibited()
? i18nc("Night Color was disabled", "Night Color Off")
: i18nc("Night Color was enabled", "Night Color On");
QDBusMessage message = QDBusMessage::createMethodCall(
QStringLiteral("org.kde.plasmashell"),
QStringLiteral("/org/kde/osdService"),
QStringLiteral("org.kde.osdService"),
QStringLiteral("showText"));
message.setArguments({ iconName, text });
QDBusConnection::sessionBus().asyncCall(message);
});
}
void Manager::init()
@ -145,7 +166,7 @@ void Manager::hardReset()
updateSunTimings(true);
}
if (kwinApp()->platform()->supportsGammaControl() && m_active) {
if (kwinApp()->platform()->supportsGammaControl() && m_active && !isInhibited()) {
m_running = true;
commitGammaRamps(currentTargetTemp());
}
@ -159,41 +180,35 @@ void Manager::reparseConfigAndReset()
hardReset();
}
// FIXME: The internal OSD service doesn't work on X11 right now. Once the QPA
// is ported away from Wayland, drop this function in favor of the internal
// OSD service.
static void showStatusOsd(bool enabled)
{
// TODO: Maybe use different icons?
const QString iconName = enabled
? QStringLiteral("preferences-desktop-display-nightcolor-on")
: QStringLiteral("preferences-desktop-display-nightcolor-off");
const QString text = enabled
? i18nc("Night Color was enabled", "Night Color On")
: i18nc("Night Color was disabled", "Night Color Off");
QDBusMessage message = QDBusMessage::createMethodCall(
QStringLiteral("org.kde.plasmashell"),
QStringLiteral("/org/kde/osdService"),
QStringLiteral("org.kde.osdService"),
QStringLiteral("showText"));
message.setArguments({ iconName, text });
QDBusConnection::sessionBus().asyncCall(message);
}
void Manager::toggle()
{
if (!kwinApp()->platform()->supportsGammaControl()) {
return;
m_isGloballyInhibited = !m_isGloballyInhibited;
m_isGloballyInhibited ? inhibit() : uninhibit();
}
bool Manager::isInhibited() const
{
return m_inhibitReferenceCount;
}
void Manager::inhibit()
{
m_inhibitReferenceCount++;
if (m_inhibitReferenceCount == 1) {
resetAllTimers();
emit inhibitedChanged();
}
}
m_active = !m_active;
void Manager::uninhibit()
{
m_inhibitReferenceCount--;
showStatusOsd(m_active);
resetAllTimers();
if (!m_inhibitReferenceCount) {
resetAllTimers();
emit inhibitedChanged();
}
}
void Manager::initShortcuts()
@ -279,9 +294,7 @@ void Manager::resetAllTimers()
{
cancelAllTimers();
if (kwinApp()->platform()->supportsGammaControl()) {
if (m_active) {
m_running = true;
}
m_running = m_active && !isInhibited();
// we do this also for active being false in order to reset the temperature back to the day value
resetQuickAdjustTimer();
} else {
@ -542,7 +555,7 @@ bool Manager::daylight() const
int Manager::currentTargetTemp() const
{
if (!m_active) {
if (!m_running) {
return NEUTRAL_TEMPERATURE;
}

View file

@ -126,6 +126,24 @@ public:
*/
void toggle();
/**
* Returns @c true if the night color manager is blocked; otherwise @c false.
*/
bool isInhibited() const;
/**
* Temporarily blocks the night color manager.
*
* After calling this method, the screen color temperature will be reverted
* back to 6500C. When you're done, call uninhibit() method.
*/
void inhibit();
/**
* Attempts to unblock the night color manager.
*/
void uninhibit();
// for auto tests
void reparseConfigAndReset();
@ -136,6 +154,11 @@ public Q_SLOTS:
Q_SIGNALS:
void configChange(QHash<QString, QVariant> data);
/**
* Emitted whenever the night color manager is blocked or unblocked.
*/
void inhibitedChanged();
private:
void initShortcuts();
void readConfig();
@ -162,9 +185,15 @@ private:
ColorCorrectDBusInterface *m_iface;
// Specifies whether Night Color is enabled.
bool m_active;
// Specifies whether Night Color is currently running.
bool m_running = false;
// Specifies whether Night Color is inhibited globally.
bool m_isGloballyInhibited = false;
NightColorMode m_mode = NightColorMode::Automatic;
// the previous and next sunrise/sunset intervals - in UTC time
@ -192,6 +221,7 @@ private:
int m_nightTargetTemp = DEFAULT_NIGHT_TEMPERATURE;
int m_failedCommitAttempts = 0;
int m_inhibitReferenceCount = 0;
// The Workspace class needs to call initShortcuts during initialization.
friend class KWin::Workspace;

View file

@ -18,5 +18,39 @@
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QHash&lt;QString,QVariant&gt;"/>
<arg type="a{sv}" direction="out"/>
</signal>
<!--
Creates an inhibition lock for Night Color.
You can use this method to temporarily disable Night Color feature.
After calling this method, the screen color temperature will be set
back to the neutral temperature (6500K).
This method returns a cookie that uniquely identifies the inhibition
request. You must pass the cookie to uninhibit() when you're done.
Note that the inhibition lock will be released automatically when
the service, which requested it, is unregistered.
A client is allowed to hold more than just one inhibition lock.
-->
<method name="inhibit">
<arg name="cookie" type="u" direction="out"/>
</method>
<!--
Releases an inhibition lock identified by the given cookie.
Note that the inhibition lock will be released automatically when
the service, which requested it, is unregistered.
-->
<method name="uninhibit">
<arg name="cookie" type="u" direction="in"/>
</method>
<!--
This property holds a value to indicate whether Night Color is inhibited.
-->
<property name="inhibited" type="b" access="read"/>
</interface>
</node>