File indexing completed on 2024-06-09 05:26:03

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "nightcolormanager.h"
0010 #include "clockskewnotifier.h"
0011 #include "colors/colordevice.h"
0012 #include "colors/colormanager.h"
0013 #include "nightcolordbusinterface.h"
0014 #include "nightcolorlogging.h"
0015 #include "nightcolorsettings.h"
0016 #include "suncalc.h"
0017 
0018 #include <core/outputbackend.h>
0019 #include <core/session.h>
0020 #include <input.h>
0021 #include <main.h>
0022 #include <workspace.h>
0023 
0024 #include <KGlobalAccel>
0025 #include <KLocalizedString>
0026 
0027 #include <QAction>
0028 #include <QDBusConnection>
0029 #include <QDBusMessage>
0030 #include <QDBusPendingReply>
0031 #include <QDBusReply>
0032 #include <QTimer>
0033 
0034 namespace KWin
0035 {
0036 
0037 static const int QUICK_ADJUST_DURATION = 2000;
0038 static const int TEMPERATURE_STEP = 50;
0039 static NightColorManager *s_instance = nullptr;
0040 
0041 static bool checkLocation(double lat, double lng)
0042 {
0043     return -90 <= lat && lat <= 90 && -180 <= lng && lng <= 180;
0044 }
0045 
0046 NightColorManager *NightColorManager::self()
0047 {
0048     return s_instance;
0049 }
0050 
0051 NightColorManager::NightColorManager()
0052 {
0053     NightColorSettings::instance(kwinApp()->config());
0054     s_instance = this;
0055 
0056     m_iface = new NightColorDBusInterface(this);
0057     m_skewNotifier = new ClockSkewNotifier(this);
0058 
0059     // Display a message when Night Color is (un)inhibited.
0060     connect(this, &NightColorManager::inhibitedChanged, this, [this] {
0061         const QString iconName = isInhibited()
0062             ? QStringLiteral("redshift-status-off")
0063             : m_daylight && m_targetTemperature != DEFAULT_DAY_TEMPERATURE ? QStringLiteral("redshift-status-day")
0064                                                                            : QStringLiteral("redshift-status-on");
0065 
0066         const QString text = isInhibited()
0067             ? i18nc("Night Light was disabled", "Night Light Off")
0068             : i18nc("Night Light was enabled", "Night Light On");
0069 
0070         QDBusMessage message = QDBusMessage::createMethodCall(
0071             QStringLiteral("org.kde.plasmashell"),
0072             QStringLiteral("/org/kde/osdService"),
0073             QStringLiteral("org.kde.osdService"),
0074             QStringLiteral("showText"));
0075         message.setArguments({iconName, text});
0076 
0077         QDBusConnection::sessionBus().asyncCall(message);
0078     });
0079 
0080     m_configWatcher = KConfigWatcher::create(kwinApp()->config());
0081     connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, &NightColorManager::reconfigure);
0082 
0083     // we may always read in the current config
0084     readConfig();
0085 
0086     QAction *toggleAction = new QAction(this);
0087     toggleAction->setProperty("componentName", QStringLiteral("kwin"));
0088     toggleAction->setObjectName(QStringLiteral("Toggle Night Color"));
0089     toggleAction->setText(i18n("Toggle Night Light"));
0090     KGlobalAccel::setGlobalShortcut(toggleAction, QList<QKeySequence>());
0091     connect(toggleAction, &QAction::triggered, this, &NightColorManager::toggle);
0092 
0093     connect(kwinApp()->colorManager(), &ColorManager::deviceAdded, this, &NightColorManager::hardReset);
0094 
0095     connect(kwinApp()->session(), &Session::activeChanged, this, [this](bool active) {
0096         if (active) {
0097             hardReset();
0098         } else {
0099             cancelAllTimers();
0100         }
0101     });
0102 
0103     connect(m_skewNotifier, &ClockSkewNotifier::clockSkewed, this, [this]() {
0104         // check if we're resuming from suspend - in this case do a hard reset
0105         // Note: We're using the time clock to detect a suspend phase instead of connecting to the
0106         //       provided logind dbus signal, because this signal would be received way too late.
0107         QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.login1",
0108                                                               "/org/freedesktop/login1",
0109                                                               "org.freedesktop.DBus.Properties",
0110                                                               QStringLiteral("Get"));
0111         message.setArguments(QVariantList({"org.freedesktop.login1.Manager", QStringLiteral("PreparingForSleep")}));
0112         QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(message);
0113         bool comingFromSuspend;
0114         if (reply.isValid()) {
0115             comingFromSuspend = reply.value().toBool();
0116         } else {
0117             qCDebug(KWIN_NIGHTCOLOR) << "Failed to get PreparingForSleep Property of logind session:" << reply.error().message();
0118             // Always do a hard reset in case we have no further information.
0119             comingFromSuspend = true;
0120         }
0121 
0122         if (comingFromSuspend) {
0123             hardReset();
0124         } else {
0125             resetAllTimers();
0126         }
0127     });
0128 
0129     hardReset();
0130 }
0131 
0132 NightColorManager::~NightColorManager()
0133 {
0134     s_instance = nullptr;
0135 }
0136 
0137 void NightColorManager::hardReset()
0138 {
0139     cancelAllTimers();
0140 
0141     updateTransitionTimings(true);
0142     updateTargetTemperature();
0143 
0144     if (isEnabled() && !isInhibited()) {
0145         setRunning(true);
0146         commitGammaRamps(currentTargetTemp());
0147     }
0148     resetAllTimers();
0149 }
0150 
0151 void NightColorManager::reconfigure()
0152 {
0153     cancelAllTimers();
0154     readConfig();
0155     resetAllTimers();
0156 }
0157 
0158 void NightColorManager::toggle()
0159 {
0160     m_isGloballyInhibited = !m_isGloballyInhibited;
0161     m_isGloballyInhibited ? inhibit() : uninhibit();
0162 }
0163 
0164 bool NightColorManager::isInhibited() const
0165 {
0166     return m_inhibitReferenceCount;
0167 }
0168 
0169 void NightColorManager::inhibit()
0170 {
0171     m_inhibitReferenceCount++;
0172 
0173     if (m_inhibitReferenceCount == 1) {
0174         resetAllTimers();
0175         Q_EMIT inhibitedChanged();
0176     }
0177 }
0178 
0179 void NightColorManager::uninhibit()
0180 {
0181     m_inhibitReferenceCount--;
0182 
0183     if (!m_inhibitReferenceCount) {
0184         resetAllTimers();
0185         Q_EMIT inhibitedChanged();
0186     }
0187 }
0188 
0189 bool NightColorManager::isEnabled() const
0190 {
0191     return m_active;
0192 }
0193 
0194 bool NightColorManager::isRunning() const
0195 {
0196     return m_running;
0197 }
0198 
0199 int NightColorManager::currentTemperature() const
0200 {
0201     return m_currentTemp;
0202 }
0203 
0204 int NightColorManager::targetTemperature() const
0205 {
0206     return m_targetTemperature;
0207 }
0208 
0209 NightColorMode NightColorManager::mode() const
0210 {
0211     return m_mode;
0212 }
0213 
0214 QDateTime NightColorManager::previousTransitionDateTime() const
0215 {
0216     return m_prev.first;
0217 }
0218 
0219 qint64 NightColorManager::previousTransitionDuration() const
0220 {
0221     return m_prev.first.msecsTo(m_prev.second);
0222 }
0223 
0224 QDateTime NightColorManager::scheduledTransitionDateTime() const
0225 {
0226     return m_next.first;
0227 }
0228 
0229 qint64 NightColorManager::scheduledTransitionDuration() const
0230 {
0231     return m_next.first.msecsTo(m_next.second);
0232 }
0233 
0234 void NightColorManager::readConfig()
0235 {
0236     NightColorSettings *s = NightColorSettings::self();
0237     s->load();
0238 
0239     setEnabled(s->active());
0240 
0241     const NightColorMode mode = s->mode();
0242     switch (s->mode()) {
0243     case NightColorMode::Automatic:
0244     case NightColorMode::Location:
0245     case NightColorMode::Timings:
0246     case NightColorMode::Constant:
0247         setMode(mode);
0248         break;
0249     default:
0250         // Fallback for invalid setting values.
0251         setMode(NightColorMode::Automatic);
0252         break;
0253     }
0254 
0255     m_dayTargetTemp = std::clamp(s->dayTemperature(), MIN_TEMPERATURE, DEFAULT_DAY_TEMPERATURE);
0256     m_nightTargetTemp = std::clamp(s->nightTemperature(), MIN_TEMPERATURE, DEFAULT_DAY_TEMPERATURE);
0257 
0258     double lat, lng;
0259     auto correctReadin = [&lat, &lng]() {
0260         if (!checkLocation(lat, lng)) {
0261             // out of domain
0262             lat = 0;
0263             lng = 0;
0264         }
0265     };
0266     // automatic
0267     lat = s->latitudeAuto();
0268     lng = s->longitudeAuto();
0269     correctReadin();
0270     m_latAuto = lat;
0271     m_lngAuto = lng;
0272     // fixed location
0273     lat = s->latitudeFixed();
0274     lng = s->longitudeFixed();
0275     correctReadin();
0276     m_latFixed = lat;
0277     m_lngFixed = lng;
0278 
0279     // fixed timings
0280     QTime mrB = QTime::fromString(s->morningBeginFixed(), "hhmm");
0281     QTime evB = QTime::fromString(s->eveningBeginFixed(), "hhmm");
0282 
0283     int diffME = evB > mrB ? mrB.msecsTo(evB) : evB.msecsTo(mrB);
0284     int diffMin = std::min(diffME, MSC_DAY - diffME);
0285 
0286     int trTime = s->transitionTime() * 1000 * 60;
0287     if (trTime < 0 || diffMin <= trTime) {
0288         // transition time too long - use defaults
0289         mrB = QTime(6, 0);
0290         evB = QTime(18, 0);
0291         trTime = FALLBACK_SLOW_UPDATE_TIME;
0292     }
0293     m_morning = mrB;
0294     m_evening = evB;
0295     m_trTime = std::max(trTime / 1000 / 60, 1);
0296 }
0297 
0298 void NightColorManager::resetAllTimers()
0299 {
0300     cancelAllTimers();
0301     setRunning(isEnabled() && !isInhibited());
0302     // we do this also for active being false in order to reset the temperature back to the day value
0303     updateTransitionTimings(false);
0304     updateTargetTemperature();
0305     resetQuickAdjustTimer(currentTargetTemp());
0306 }
0307 
0308 void NightColorManager::cancelAllTimers()
0309 {
0310     m_slowUpdateStartTimer.reset();
0311     m_slowUpdateTimer.reset();
0312     m_quickAdjustTimer.reset();
0313 }
0314 
0315 void NightColorManager::resetQuickAdjustTimer(int targetTemp)
0316 {
0317     int tempDiff = std::abs(targetTemp - m_currentTemp);
0318     // allow tolerance of one TEMPERATURE_STEP to compensate if a slow update is coincidental
0319     if (tempDiff > TEMPERATURE_STEP) {
0320         cancelAllTimers();
0321         m_quickAdjustTimer = std::make_unique<QTimer>();
0322         m_quickAdjustTimer->setSingleShot(false);
0323         connect(m_quickAdjustTimer.get(), &QTimer::timeout, this, [this, targetTemp]() {
0324             quickAdjust(targetTemp);
0325         });
0326 
0327         int interval = (QUICK_ADJUST_DURATION / (m_previewTimer && m_previewTimer->isActive() ? 8 : 1)) / (tempDiff / TEMPERATURE_STEP);
0328         if (interval == 0) {
0329             interval = 1;
0330         }
0331         m_quickAdjustTimer->start(interval);
0332     } else {
0333         resetSlowUpdateStartTimer();
0334     }
0335 }
0336 
0337 void NightColorManager::quickAdjust(int targetTemp)
0338 {
0339     if (!m_quickAdjustTimer) {
0340         return;
0341     }
0342 
0343     int nextTemp;
0344 
0345     if (m_currentTemp < targetTemp) {
0346         nextTemp = std::min(m_currentTemp + TEMPERATURE_STEP, targetTemp);
0347     } else {
0348         nextTemp = std::max(m_currentTemp - TEMPERATURE_STEP, targetTemp);
0349     }
0350     commitGammaRamps(nextTemp);
0351 
0352     if (nextTemp == targetTemp) {
0353         // stop timer, we reached the target temp
0354         m_quickAdjustTimer.reset();
0355         resetSlowUpdateStartTimer();
0356     }
0357 }
0358 
0359 void NightColorManager::resetSlowUpdateStartTimer()
0360 {
0361     m_slowUpdateStartTimer.reset();
0362 
0363     if (!m_running || m_quickAdjustTimer) {
0364         // only reenable the slow update start timer when quick adjust is not active anymore
0365         return;
0366     }
0367 
0368     // There is no need for starting the slow update timer. Screen color temperature
0369     // will be constant all the time now.
0370     if (m_mode == NightColorMode::Constant) {
0371         return;
0372     }
0373 
0374     // set up the next slow update
0375     m_slowUpdateStartTimer = std::make_unique<QTimer>();
0376     m_slowUpdateStartTimer->setSingleShot(true);
0377     connect(m_slowUpdateStartTimer.get(), &QTimer::timeout, this, &NightColorManager::resetSlowUpdateStartTimer);
0378 
0379     updateTransitionTimings(false);
0380     updateTargetTemperature();
0381 
0382     const int diff = QDateTime::currentDateTime().msecsTo(m_next.first);
0383     if (diff <= 0) {
0384         qCCritical(KWIN_NIGHTCOLOR) << "Error in time calculation. Deactivating Night Color.";
0385         return;
0386     }
0387     m_slowUpdateStartTimer->start(diff);
0388 
0389     // start the current slow update
0390     resetSlowUpdateTimer();
0391 }
0392 
0393 void NightColorManager::resetSlowUpdateTimer()
0394 {
0395     m_slowUpdateTimer.reset();
0396 
0397     const QDateTime now = QDateTime::currentDateTime();
0398     const bool isDay = daylight();
0399     const int targetTemp = isDay ? m_dayTargetTemp : m_nightTargetTemp;
0400 
0401     // We've reached the target color temperature or the transition time is zero.
0402     if (m_prev.first == m_prev.second || m_currentTemp == targetTemp) {
0403         commitGammaRamps(targetTemp);
0404         return;
0405     }
0406 
0407     if (m_prev.first <= now && now <= m_prev.second) {
0408         int availTime = now.msecsTo(m_prev.second);
0409         m_slowUpdateTimer = std::make_unique<QTimer>();
0410         m_slowUpdateTimer->setSingleShot(false);
0411         if (isDay) {
0412             connect(m_slowUpdateTimer.get(), &QTimer::timeout, this, [this]() {
0413                 slowUpdate(m_dayTargetTemp);
0414             });
0415         } else {
0416             connect(m_slowUpdateTimer.get(), &QTimer::timeout, this, [this]() {
0417                 slowUpdate(m_nightTargetTemp);
0418             });
0419         }
0420 
0421         // calculate interval such as temperature is changed by TEMPERATURE_STEP K per timer timeout
0422         int interval = availTime * TEMPERATURE_STEP / std::abs(targetTemp - m_currentTemp);
0423         if (interval == 0) {
0424             interval = 1;
0425         }
0426         m_slowUpdateTimer->start(interval);
0427     }
0428 }
0429 
0430 void NightColorManager::slowUpdate(int targetTemp)
0431 {
0432     if (!m_slowUpdateTimer) {
0433         return;
0434     }
0435     int nextTemp;
0436     if (m_currentTemp < targetTemp) {
0437         nextTemp = std::min(m_currentTemp + TEMPERATURE_STEP, targetTemp);
0438     } else {
0439         nextTemp = std::max(m_currentTemp - TEMPERATURE_STEP, targetTemp);
0440     }
0441     commitGammaRamps(nextTemp);
0442     if (nextTemp == targetTemp) {
0443         // stop timer, we reached the target temp
0444         m_slowUpdateTimer.reset();
0445     }
0446 }
0447 
0448 void NightColorManager::preview(uint previewTemp)
0449 {
0450     previewTemp = std::clamp<uint>(previewTemp, MIN_TEMPERATURE, DEFAULT_DAY_TEMPERATURE);
0451     resetQuickAdjustTimer((int)previewTemp);
0452     if (m_previewTimer) {
0453         m_previewTimer.reset();
0454     }
0455     m_previewTimer = std::make_unique<QTimer>();
0456     m_previewTimer->setSingleShot(true);
0457     connect(m_previewTimer.get(), &QTimer::timeout, this, &NightColorManager::stopPreview);
0458     m_previewTimer->start(15000);
0459 
0460     QDBusMessage message = QDBusMessage::createMethodCall(
0461         QStringLiteral("org.kde.plasmashell"),
0462         QStringLiteral("/org/kde/osdService"),
0463         QStringLiteral("org.kde.osdService"),
0464         QStringLiteral("showText"));
0465     message.setArguments(
0466         {QStringLiteral("redshift-status-on"),
0467          i18n("Color Temperature Preview")});
0468     QDBusConnection::sessionBus().asyncCall(message);
0469 }
0470 
0471 void NightColorManager::stopPreview()
0472 {
0473     if (m_previewTimer && m_previewTimer->isActive()) {
0474         updateTransitionTimings(false);
0475         updateTargetTemperature();
0476         resetQuickAdjustTimer(currentTargetTemp());
0477     }
0478 }
0479 
0480 void NightColorManager::updateTargetTemperature()
0481 {
0482     const int targetTemperature = mode() != NightColorMode::Constant && daylight() ? m_dayTargetTemp : m_nightTargetTemp;
0483 
0484     if (m_targetTemperature == targetTemperature) {
0485         return;
0486     }
0487 
0488     m_targetTemperature = targetTemperature;
0489 
0490     Q_EMIT targetTemperatureChanged();
0491 }
0492 
0493 void NightColorManager::updateTransitionTimings(bool force)
0494 {
0495     const auto oldPrev = m_prev;
0496     const auto oldNext = m_next;
0497 
0498     if (m_mode == NightColorMode::Constant) {
0499         setDaylight(false);
0500         m_next = DateTimes();
0501         m_prev = DateTimes();
0502     } else if (m_mode == NightColorMode::Timings) {
0503         const QDateTime todayNow = QDateTime::currentDateTime();
0504 
0505         const QDateTime nextMorB = QDateTime(todayNow.date().addDays(m_morning < todayNow.time()), m_morning);
0506         const QDateTime nextMorE = nextMorB.addSecs(m_trTime * 60);
0507         const QDateTime nextEveB = QDateTime(todayNow.date().addDays(m_evening < todayNow.time()), m_evening);
0508         const QDateTime nextEveE = nextEveB.addSecs(m_trTime * 60);
0509 
0510         if (nextEveB < nextMorB) {
0511             setDaylight(true);
0512             m_next = DateTimes(nextEveB, nextEveE);
0513             m_prev = DateTimes(nextMorB.addDays(-1), nextMorE.addDays(-1));
0514         } else {
0515             setDaylight(false);
0516             m_next = DateTimes(nextMorB, nextMorE);
0517             m_prev = DateTimes(nextEveB.addDays(-1), nextEveE.addDays(-1));
0518         }
0519     } else {
0520         const QDateTime todayNow = QDateTime::currentDateTime();
0521 
0522         double lat, lng;
0523         if (m_mode == NightColorMode::Automatic) {
0524             lat = m_latAuto;
0525             lng = m_lngAuto;
0526         } else {
0527             lat = m_latFixed;
0528             lng = m_lngFixed;
0529         }
0530 
0531         if (!force) {
0532             // first try by only switching the timings
0533             if (m_prev.first.date() == m_next.first.date()) {
0534                 // next is evening
0535                 setDaylight(true);
0536                 m_prev = m_next;
0537                 m_next = getSunTimings(todayNow, lat, lng, false);
0538             } else {
0539                 // next is morning
0540                 setDaylight(false);
0541                 m_prev = m_next;
0542                 m_next = getSunTimings(todayNow.addDays(1), lat, lng, true);
0543             }
0544         }
0545 
0546         if (force || !checkAutomaticSunTimings()) {
0547             // in case this fails, reset them
0548             DateTimes morning = getSunTimings(todayNow, lat, lng, true);
0549             if (todayNow < morning.first) {
0550                 setDaylight(false);
0551                 m_prev = getSunTimings(todayNow.addDays(-1), lat, lng, false);
0552                 m_next = morning;
0553             } else {
0554                 DateTimes evening = getSunTimings(todayNow, lat, lng, false);
0555                 if (todayNow < evening.first) {
0556                     setDaylight(true);
0557                     m_prev = morning;
0558                     m_next = evening;
0559                 } else {
0560                     setDaylight(false);
0561                     m_prev = evening;
0562                     m_next = getSunTimings(todayNow.addDays(1), lat, lng, true);
0563                 }
0564             }
0565         }
0566     }
0567 
0568     if (oldPrev != m_prev) {
0569         Q_EMIT previousTransitionTimingsChanged();
0570     }
0571     if (oldNext != m_next) {
0572         Q_EMIT scheduledTransitionTimingsChanged();
0573     }
0574 }
0575 
0576 DateTimes NightColorManager::getSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning) const
0577 {
0578     DateTimes dateTimes = calculateSunTimings(dateTime, latitude, longitude, morning);
0579     // At locations near the poles it is possible, that we can't
0580     // calculate some or all sun timings (midnight sun).
0581     // In this case try to fallback to sensible default values.
0582     const bool beginDefined = !dateTimes.first.isNull();
0583     const bool endDefined = !dateTimes.second.isNull();
0584     if (!beginDefined || !endDefined) {
0585         if (beginDefined) {
0586             dateTimes.second = dateTimes.first.addMSecs(FALLBACK_SLOW_UPDATE_TIME);
0587         } else if (endDefined) {
0588             dateTimes.first = dateTimes.second.addMSecs(-FALLBACK_SLOW_UPDATE_TIME);
0589         } else {
0590             // Just use default values for morning and evening, but the user
0591             // will probably deactivate Night Color anyway if he is living
0592             // in a region without clear sun rise and set.
0593             const QTime referenceTime = morning ? QTime(6, 0) : QTime(18, 0);
0594             dateTimes.first = QDateTime(dateTime.date(), referenceTime);
0595             dateTimes.second = dateTimes.first.addMSecs(FALLBACK_SLOW_UPDATE_TIME);
0596         }
0597     }
0598     return dateTimes;
0599 }
0600 
0601 bool NightColorManager::checkAutomaticSunTimings() const
0602 {
0603     if (m_prev.first.isValid() && m_prev.second.isValid() && m_next.first.isValid() && m_next.second.isValid()) {
0604         const QDateTime todayNow = QDateTime::currentDateTime();
0605         return m_prev.first <= todayNow && todayNow < m_next.first && m_prev.first.msecsTo(m_next.first) < MSC_DAY * 23. / 24;
0606     }
0607     return false;
0608 }
0609 
0610 bool NightColorManager::daylight() const
0611 {
0612     return m_daylight;
0613 }
0614 
0615 int NightColorManager::currentTargetTemp() const
0616 {
0617     if (!m_running) {
0618         return DEFAULT_DAY_TEMPERATURE;
0619     }
0620 
0621     if (m_mode == NightColorMode::Constant) {
0622         return m_nightTargetTemp;
0623     }
0624 
0625     const QDateTime todayNow = QDateTime::currentDateTime();
0626 
0627     auto f = [this, todayNow](int target1, int target2) {
0628         if (todayNow <= m_prev.second) {
0629             double residueQuota = todayNow.msecsTo(m_prev.second) / (double)m_prev.first.msecsTo(m_prev.second);
0630 
0631             double ret = (int)((1. - residueQuota) * (double)target2 + residueQuota * (double)target1);
0632             // remove single digits
0633             ret = ((int)(0.1 * ret)) * 10;
0634             return (int)ret;
0635         } else {
0636             return target2;
0637         }
0638     };
0639 
0640     if (daylight()) {
0641         return f(m_nightTargetTemp, m_dayTargetTemp);
0642     } else {
0643         return f(m_dayTargetTemp, m_nightTargetTemp);
0644     }
0645 }
0646 
0647 void NightColorManager::commitGammaRamps(int temperature)
0648 {
0649     const QList<ColorDevice *> devices = kwinApp()->colorManager()->devices();
0650     for (ColorDevice *device : devices) {
0651         device->setTemperature(temperature);
0652     }
0653 
0654     setCurrentTemperature(temperature);
0655 }
0656 
0657 void NightColorManager::autoLocationUpdate(double latitude, double longitude)
0658 {
0659     qCDebug(KWIN_NIGHTCOLOR, "Received new location (lat: %f, lng: %f)", latitude, longitude);
0660 
0661     if (!checkLocation(latitude, longitude)) {
0662         return;
0663     }
0664 
0665     // we tolerate small deviations with minimal impact on sun timings
0666     if (std::abs(m_latAuto - latitude) < 2 && std::abs(m_lngAuto - longitude) < 1) {
0667         return;
0668     }
0669     cancelAllTimers();
0670     m_latAuto = latitude;
0671     m_lngAuto = longitude;
0672 
0673     NightColorSettings *s = NightColorSettings::self();
0674     s->setLatitudeAuto(latitude);
0675     s->setLongitudeAuto(longitude);
0676     s->save();
0677 
0678     resetAllTimers();
0679 }
0680 
0681 void NightColorManager::setEnabled(bool enabled)
0682 {
0683     if (m_active == enabled) {
0684         return;
0685     }
0686     m_active = enabled;
0687     m_skewNotifier->setActive(enabled);
0688     Q_EMIT enabledChanged();
0689 }
0690 
0691 void NightColorManager::setRunning(bool running)
0692 {
0693     if (m_running == running) {
0694         return;
0695     }
0696     m_running = running;
0697     Q_EMIT runningChanged();
0698 }
0699 
0700 void NightColorManager::setCurrentTemperature(int temperature)
0701 {
0702     if (m_currentTemp == temperature) {
0703         return;
0704     }
0705     m_currentTemp = temperature;
0706     Q_EMIT currentTemperatureChanged();
0707 }
0708 
0709 void NightColorManager::setMode(NightColorMode mode)
0710 {
0711     if (m_mode == mode) {
0712         return;
0713     }
0714     m_mode = mode;
0715     Q_EMIT modeChanged();
0716 }
0717 
0718 void NightColorManager::setDaylight(bool daylight)
0719 {
0720     if (m_daylight == daylight) {
0721         return;
0722     }
0723     m_daylight = daylight;
0724     Q_EMIT daylightChanged();
0725 }
0726 
0727 } // namespace KWin
0728 
0729 #include "moc_nightcolormanager.cpp"