diff --git a/colorcorrection/colorcorrectdbusinterface.h b/colorcorrection/colorcorrectdbusinterface.h --- a/colorcorrection/colorcorrectdbusinterface.h +++ b/colorcorrection/colorcorrectdbusinterface.h @@ -32,15 +32,18 @@ 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 @@ * @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 @@ */ void nightColorConfigChanged(QHash data); +private Q_SLOTS: + void removeInhibitorService(const QString &serviceName); + private: + void uninhibit(const QString &serviceName, uint cookie); + Manager *m_manager; + QDBusServiceWatcher *m_inhibitorWatcher; + QMultiHash m_inhibitors; + uint m_lastInhibitionCookie = 0; }; } diff --git a/colorcorrection/colorcorrectdbusinterface.cpp b/colorcorrection/colorcorrectdbusinterface.cpp --- a/colorcorrection/colorcorrectdbusinterface.cpp +++ b/colorcorrection/colorcorrectdbusinterface.cpp @@ -23,18 +23,51 @@ #include "manager.h" +#include + 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 ColorCorrectDBusInterface::nightColorInfo() { return m_manager->info(); @@ -50,5 +83,49 @@ 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); + } +} + } } diff --git a/colorcorrection/manager.h b/colorcorrection/manager.h --- a/colorcorrection/manager.h +++ b/colorcorrection/manager.h @@ -126,6 +126,24 @@ */ 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 @@ Q_SIGNALS: void configChange(QHash data); + /** + * Emitted whenever the night color manager is blocked or unblocked. + */ + void inhibitedChanged(); + private: void initShortcuts(); void readConfig(); @@ -162,9 +185,15 @@ 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 @@ 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; diff --git a/colorcorrection/manager.cpp b/colorcorrection/manager.cpp --- a/colorcorrection/manager.cpp +++ b/colorcorrection/manager.cpp @@ -61,6 +61,27 @@ { 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 @@ updateSunTimings(true); } - if (kwinApp()->platform()->supportsGammaControl() && m_active) { + if (kwinApp()->platform()->supportsGammaControl() && m_active && !isInhibited()) { m_running = true; commitGammaRamps(currentTargetTemp()); } @@ -159,41 +180,35 @@ 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) +void Manager::toggle() { - // 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); + m_isGloballyInhibited = !m_isGloballyInhibited; + m_isGloballyInhibited ? inhibit() : uninhibit(); } -void Manager::toggle() +bool Manager::isInhibited() const { - if (!kwinApp()->platform()->supportsGammaControl()) { - return; - } + return m_inhibitReferenceCount; +} + +void Manager::inhibit() +{ + m_inhibitReferenceCount++; - m_active = !m_active; + if (m_inhibitReferenceCount == 1) { + resetAllTimers(); + emit inhibitedChanged(); + } +} - showStatusOsd(m_active); +void Manager::uninhibit() +{ + m_inhibitReferenceCount--; - resetAllTimers(); + if (!m_inhibitReferenceCount) { + resetAllTimers(); + emit inhibitedChanged(); + } } void Manager::initShortcuts() @@ -279,9 +294,7 @@ { 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 @@ int Manager::currentTargetTemp() const { - if (!m_active) { + if (!m_running) { return NEUTRAL_TEMPERATURE; } diff --git a/org.kde.kwin.ColorCorrect.xml b/org.kde.kwin.ColorCorrect.xml --- a/org.kde.kwin.ColorCorrect.xml +++ b/org.kde.kwin.ColorCorrect.xml @@ -18,5 +18,39 @@ + + + + + + + + + + + + +