[colorcorrection] Night Color - blue light filter at nighttime
With Wayland KWin needs to provide certain services, which were provided before that by the Xserver. One of these is gamma correction, which includes the - by many people beloved - functionality to reduce the blue light at nighttime. This patch provides the KWin part of that. It is self contained, but in the end will work in tandem with a lib in Plasma Workspace and a KCM in Plasma Desktop, which can be used to configure Night Color. * Three modi: ** Automatic: The location and sun timings are determined automatically (location data updates will be provided by the workspace) ** Location: The sun timings are determined by fixed location data ** Timings: The sun timings are set manually by the user * Color temperature value changes are smoothly applied: ** Configuration changes, which lead to other current values are changed in a quick way over a few seconds ** Changes on sunrise and sunset are applied slowly over the course of few minutes till several hours depending on the configuration * The current color value is set immediately at startup or after suspend phases and VT switches. There is no flickering. * All configuration is done via a DBus interface, changed values are tested on correctness and applied atomically * Self contained mechanism, speaks directly to the hardware by setting the gamma ramps on the CRTC * Currently working on DRM backend, extensible to other platform backends in the future * The code is written in a way to make the classes later easily extendable to also provide normal color correction, as it's currently done by KGamma on X Test Plan: Manually with the workspace parts and added integration tests in KWin using the virtual backend. BUG:371494 Reviewers: #kwin, graesslin Subscribers: kwin, plasma-devel, #kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5928icc-effect-5.14.5
parent
c9023582aa
commit
82d2860469
|
@ -471,6 +471,9 @@ set(kwin_KDEINIT_SRCS
|
|||
xkb.cpp
|
||||
gestures.cpp
|
||||
popup_input_filter.cpp
|
||||
colorcorrection/manager.cpp
|
||||
colorcorrection/colorcorrectdbusinterface.cpp
|
||||
colorcorrection/suncalc.cpp
|
||||
abstract_opengl_context_attribute_builder.cpp
|
||||
egl_context_attribute_builder.cpp
|
||||
was_user_interaction_x11_filter.cpp
|
||||
|
@ -481,6 +484,18 @@ set(kwin_KDEINIT_SRCS
|
|||
idle_inhibition.cpp
|
||||
)
|
||||
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
ecm_qt_declare_logging_category(kwin_KDEINIT_SRCS
|
||||
HEADER
|
||||
colorcorrect_logging.h
|
||||
IDENTIFIER
|
||||
KWIN_COLORCORRECTION
|
||||
CATEGORY_NAME
|
||||
kwin_colorcorrection
|
||||
DEFAULT_SEVERITY
|
||||
Critical
|
||||
)
|
||||
|
||||
if(KWIN_BUILD_TABBOX)
|
||||
set(
|
||||
kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS}
|
||||
|
@ -528,9 +543,11 @@ if(HAVE_INPUT)
|
|||
endif()
|
||||
|
||||
kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc)
|
||||
kconfig_add_kcfg_files(kwin_KDEINIT_SRCS colorcorrection/colorcorrect_settings.kcfgc)
|
||||
|
||||
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h KWin::DBusInterface )
|
||||
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Compositing.xml dbusinterface.h KWin::CompositorDBusInterface )
|
||||
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/colorcorrectdbusinterface.h KWin::ColorCorrect::ColorCorrectDBusInterface )
|
||||
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl )
|
||||
|
||||
qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface)
|
||||
|
@ -668,11 +685,13 @@ add_subdirectory(plugins)
|
|||
########### install files ###############
|
||||
|
||||
install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}.kcfg )
|
||||
install( FILES colorcorrection/colorcorrect_settings.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}_colorcorrect.kcfg )
|
||||
install( FILES kwin.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR} RENAME ${KWIN_NAME}.notifyrc)
|
||||
install(
|
||||
FILES
|
||||
org.kde.KWin.xml
|
||||
org.kde.kwin.Compositing.xml
|
||||
org.kde.kwin.ColorCorrect.xml
|
||||
org.kde.kwin.Effects.xml
|
||||
DESTINATION
|
||||
${KDE_INSTALL_DBUSINTERFACEDIR}
|
||||
|
|
|
@ -54,6 +54,7 @@ integrationTest(WAYLAND_ONLY NAME testKWinBindings SRCS kwinbindings_test.cpp)
|
|||
integrationTest(WAYLAND_ONLY NAME testVirtualDesktop SRCS virtual_desktop_test.cpp)
|
||||
integrationTest(WAYLAND_ONLY NAME testShellClientRules SRCS shell_client_rules_test.cpp)
|
||||
integrationTest(WAYLAND_ONLY NAME testIdleInhibition SRCS idle_inhibition_test.cpp)
|
||||
integrationTest(WAYLAND_ONLY NAME testColorCorrectNightColor SRCS colorcorrect_nightcolor_test.cpp)
|
||||
|
||||
if (XCB_ICCCM_FOUND)
|
||||
integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM)
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "platform.h"
|
||||
#include "wayland_server.h"
|
||||
#include "colorcorrection/manager.h"
|
||||
#include "colorcorrection/constants.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
|
||||
using namespace KWin;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_colorcorrect_nightcolor-0");
|
||||
|
||||
class ColorCorrectNightColorTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testConfigRead_data();
|
||||
void testConfigRead();
|
||||
void testChangeConfiguration_data();
|
||||
void testChangeConfiguration();
|
||||
void testAutoLocationUpdate();
|
||||
};
|
||||
|
||||
void ColorCorrectNightColorTest::initTestCase()
|
||||
{
|
||||
QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
|
||||
QVERIFY(workspaceCreatedSpy.isValid());
|
||||
QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2));
|
||||
QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
|
||||
|
||||
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
|
||||
|
||||
kwinApp()->start();
|
||||
QVERIFY(workspaceCreatedSpy.wait());
|
||||
waylandServer()->initWorkspace();
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::init()
|
||||
{
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::testConfigRead_data()
|
||||
{
|
||||
QTest::addColumn<QString>("active");
|
||||
QTest::addColumn<int>("mode");
|
||||
QTest::addColumn<int>("nightTemperature");
|
||||
QTest::addColumn<double>("latitudeFixed");
|
||||
QTest::addColumn<double>("longitudeFixed");
|
||||
QTest::addColumn<QString>("morningBeginFixed");
|
||||
QTest::addColumn<QString>("eveningBeginFixed");
|
||||
QTest::addColumn<int>("transitionTime");
|
||||
QTest::addColumn<bool>("success");
|
||||
|
||||
QTest::newRow("activeMode0") << "true" << 0 << 4500 << 45.5 << 35.1 << "0600" << "1800" << 30 << true;
|
||||
QTest::newRow("activeMode1") << "true" << 1 << 2500 << -10.5 << -8. << "0020" << "2000" << 60 << true;
|
||||
QTest::newRow("notActiveMode2") << "false" << 2 << 5000 << 90. << -180. << "0600" << "1800" << 1 << true;
|
||||
QTest::newRow("wrongData1") << "fa" << 3 << 7000 << 91. << -181. << "060" << "800" << 999999 << false;
|
||||
QTest::newRow("wrongData2") << "fa" << 3 << 7000 << 91. << -181. << "060" << "800" << -2 << false;
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::testConfigRead()
|
||||
{
|
||||
QFETCH(QString, active);
|
||||
QFETCH(int, mode);
|
||||
QFETCH(int, nightTemperature);
|
||||
QFETCH(double, latitudeFixed);
|
||||
QFETCH(double, longitudeFixed);
|
||||
QFETCH(QString, morningBeginFixed);
|
||||
QFETCH(QString, eveningBeginFixed);
|
||||
QFETCH(int, transitionTime);
|
||||
QFETCH(bool, success);
|
||||
|
||||
const bool activeDefault = true;
|
||||
const int modeDefault = 0;
|
||||
const int nightTemperatureUpperEnd = ColorCorrect::NEUTRAL_TEMPERATURE;
|
||||
const double latitudeFixedDefault = 0;
|
||||
const double longitudeFixedDefault = 0;
|
||||
const QTime morningBeginFixedDefault = QTime(6,0,0);
|
||||
const QTime eveningBeginFixedDefault = QTime(18,0,0);
|
||||
const int transitionTimeDefault = 30;
|
||||
|
||||
KConfigGroup cfgGroup = kwinApp()->config()->group("NightColor");
|
||||
|
||||
cfgGroup.writeEntry("Active", activeDefault);
|
||||
cfgGroup.writeEntry("Mode", modeDefault);
|
||||
cfgGroup.writeEntry("NightTemperature", nightTemperatureUpperEnd);
|
||||
cfgGroup.writeEntry("LatitudeFixed", latitudeFixedDefault);
|
||||
cfgGroup.writeEntry("LongitudeFixed", longitudeFixedDefault);
|
||||
cfgGroup.writeEntry("MorningBeginFixed", morningBeginFixedDefault.toString("hhmm"));
|
||||
cfgGroup.writeEntry("EveningBeginFixed", eveningBeginFixedDefault.toString("hhmm"));
|
||||
cfgGroup.writeEntry("TransitionTime", transitionTimeDefault);
|
||||
|
||||
ColorCorrect::Manager *manager = kwinApp()->platform()->colorCorrectManager();
|
||||
manager->reparseConfigAndReset();
|
||||
auto info = manager->info();
|
||||
QVERIFY(!info.isEmpty());
|
||||
|
||||
QCOMPARE(info.value("Active").toBool(), activeDefault);
|
||||
QCOMPARE(info.value("Mode").toInt(), modeDefault);
|
||||
QCOMPARE(info.value("NightTemperature").toInt(), nightTemperatureUpperEnd);
|
||||
QCOMPARE(info.value("LatitudeFixed").toDouble(), latitudeFixedDefault);
|
||||
QCOMPARE(info.value("LongitudeFixed").toDouble(), longitudeFixedDefault);
|
||||
QCOMPARE(QTime::fromString(info.value("MorningBeginFixed").toString(), Qt::ISODate), morningBeginFixedDefault);
|
||||
QCOMPARE(QTime::fromString(info.value("EveningBeginFixed").toString(), Qt::ISODate), eveningBeginFixedDefault);
|
||||
QCOMPARE(info.value("TransitionTime").toInt(), transitionTimeDefault);
|
||||
|
||||
cfgGroup.writeEntry("Active", active);
|
||||
cfgGroup.writeEntry("Mode", mode);
|
||||
cfgGroup.writeEntry("NightTemperature", nightTemperature);
|
||||
cfgGroup.writeEntry("LatitudeFixed", latitudeFixed);
|
||||
cfgGroup.writeEntry("LongitudeFixed", longitudeFixed);
|
||||
cfgGroup.writeEntry("MorningBeginFixed", morningBeginFixed);
|
||||
cfgGroup.writeEntry("EveningBeginFixed", eveningBeginFixed);
|
||||
cfgGroup.writeEntry("TransitionTime", transitionTime);
|
||||
|
||||
manager->reparseConfigAndReset();
|
||||
info = manager->info();
|
||||
QVERIFY(!info.isEmpty());
|
||||
|
||||
if (success) {
|
||||
QCOMPARE(info.value("Active").toBool() ? QString("true") : QString("false"), active);
|
||||
QCOMPARE(info.value("Mode").toInt(), mode);
|
||||
QCOMPARE(info.value("NightTemperature").toInt(), nightTemperature);
|
||||
QCOMPARE(info.value("LatitudeFixed").toDouble(), latitudeFixed);
|
||||
QCOMPARE(info.value("LongitudeFixed").toDouble(), longitudeFixed);
|
||||
QCOMPARE(QTime::fromString(info.value("MorningBeginFixed").toString(), Qt::ISODate), QTime::fromString(morningBeginFixed, "hhmm"));
|
||||
QCOMPARE(QTime::fromString(info.value("EveningBeginFixed").toString(), Qt::ISODate), QTime::fromString(eveningBeginFixed, "hhmm"));
|
||||
QCOMPARE(info.value("TransitionTime").toInt(), transitionTime);
|
||||
} else {
|
||||
QCOMPARE(info.value("Active").toBool(), activeDefault);
|
||||
QCOMPARE(info.value("Mode").toInt(), modeDefault);
|
||||
QCOMPARE(info.value("NightTemperature").toInt(), nightTemperatureUpperEnd);
|
||||
QCOMPARE(info.value("LatitudeFixed").toDouble(), latitudeFixedDefault);
|
||||
QCOMPARE(info.value("LongitudeFixed").toDouble(), longitudeFixedDefault);
|
||||
QCOMPARE(QTime::fromString(info.value("MorningBeginFixed").toString(), Qt::ISODate), morningBeginFixedDefault);
|
||||
QCOMPARE(QTime::fromString(info.value("EveningBeginFixed").toString(), Qt::ISODate), eveningBeginFixedDefault);
|
||||
QCOMPARE(info.value("TransitionTime").toInt(), transitionTimeDefault);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::testChangeConfiguration_data()
|
||||
{
|
||||
QTest::addColumn<bool>("activeReadIn");
|
||||
QTest::addColumn<int>("modeReadIn");
|
||||
QTest::addColumn<int>("nightTemperatureReadIn");
|
||||
QTest::addColumn<double>("latitudeFixedReadIn");
|
||||
QTest::addColumn<double>("longitudeFixedReadIn");
|
||||
QTest::addColumn<QTime>("morBeginFixedReadIn");
|
||||
QTest::addColumn<QTime>("eveBeginFixedReadIn");
|
||||
QTest::addColumn<int>("transitionTimeReadIn");
|
||||
QTest::addColumn<bool>("successReadIn");
|
||||
|
||||
QTest::newRow("data0") << true << 0 << 4500 << 45.5 << 35.1 << QTime(6,0,0) << QTime(18,0,0) << 30 << true;
|
||||
QTest::newRow("data1") << true << 1 << 2500 << -10.5 << -8. << QTime(0,2,0) << QTime(20,0,0) << 60 << true;
|
||||
QTest::newRow("data2") << false << 2 << 5000 << 90. << -180. << QTime(6,0,0) << QTime(19,1,1) << 1 << true;
|
||||
QTest::newRow("wrongData0") << true << 3 << 4500 << 0. << 0. << QTime(6,0,0) << QTime(18,0,0) << 30 << false;
|
||||
QTest::newRow("wrongData1") << true << 0 << 500 << 0. << 0. << QTime(6,0,0) << QTime(18,0,0) << 30 << false;
|
||||
QTest::newRow("wrongData2") << true << 0 << 7000 << 0. << 0. << QTime(6,0,0) << QTime(18,0,0) << 30 << false;
|
||||
QTest::newRow("wrongData3") << true << 0 << 4500 << 91. << -181. << QTime(6,0,0) << QTime(18,0,0) << 30 << false;
|
||||
QTest::newRow("wrongData4") << true << 0 << 4500 << 0. << 0. << QTime(18,0,0) << QTime(6,0,0) << 30 << false;
|
||||
QTest::newRow("wrongData5") << true << 0 << 4500 << 0. << 0. << QTime(6,0,0) << QTime(18,0,0) << 0 << false;
|
||||
QTest::newRow("wrongData6") << true << 0 << 4500 << 0. << 0. << QTime(6,0,0) << QTime(18,0,0) << -1 << false;
|
||||
QTest::newRow("wrongData7") << true << 0 << 4500 << 0. << 0. << QTime(12,0,0) << QTime(12,30,0) << 30 << false;
|
||||
QTest::newRow("wrongData8") << true << 0 << 4500 << 0. << 0. << QTime(1,0,0) << QTime(23,30,0) << 90 << false;
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::testChangeConfiguration()
|
||||
{
|
||||
QFETCH(bool, activeReadIn);
|
||||
QFETCH(int, modeReadIn);
|
||||
QFETCH(int, nightTemperatureReadIn);
|
||||
QFETCH(double, latitudeFixedReadIn);
|
||||
QFETCH(double, longitudeFixedReadIn);
|
||||
QFETCH(QTime, morBeginFixedReadIn);
|
||||
QFETCH(QTime, eveBeginFixedReadIn);
|
||||
QFETCH(int, transitionTimeReadIn);
|
||||
QFETCH(bool, successReadIn);
|
||||
|
||||
const bool activeDefault = true;
|
||||
const int modeDefault = 0;
|
||||
const int nightTemperatureDefault = ColorCorrect::DEFAULT_NIGHT_TEMPERATURE;
|
||||
const double latitudeFixedDefault = 0;
|
||||
const double longitudeFixedDefault = 0;
|
||||
const QTime morningBeginFixedDefault = QTime(6,0,0);
|
||||
const QTime eveningBeginFixedDefault = QTime(18,0,0);
|
||||
const int transitionTimeDefault = 30;
|
||||
|
||||
// init with default values
|
||||
bool active = activeDefault;
|
||||
int mode = modeDefault;
|
||||
int nightTemperature = nightTemperatureDefault;
|
||||
double latitudeFixed = latitudeFixedDefault;
|
||||
double longitudeFixed = longitudeFixedDefault;
|
||||
QTime morningBeginFixed = morningBeginFixedDefault;
|
||||
QTime eveningBeginFixed = eveningBeginFixedDefault;
|
||||
int transitionTime = transitionTimeDefault;
|
||||
|
||||
bool activeExpect = activeDefault;
|
||||
int modeExpect = modeDefault;
|
||||
int nightTemperatureExpect = nightTemperatureDefault;
|
||||
double latitudeFixedExpect = latitudeFixedDefault;
|
||||
double longitudeFixedExpect = longitudeFixedDefault;
|
||||
QTime morningBeginFixedExpect = morningBeginFixedDefault;
|
||||
QTime eveningBeginFixedExpect = eveningBeginFixedDefault;
|
||||
int transitionTimeExpect = transitionTimeDefault;
|
||||
|
||||
QHash<QString, QVariant> data;
|
||||
|
||||
auto setData = [&active, &mode, &nightTemperature,
|
||||
&latitudeFixed, &longitudeFixed,
|
||||
&morningBeginFixed, &eveningBeginFixed, &transitionTime,
|
||||
&data]() {
|
||||
data["Active"] = active;
|
||||
data["Mode"] = mode;
|
||||
data["NightTemperature"] = nightTemperature;
|
||||
|
||||
data["LatitudeFixed"] = latitudeFixed;
|
||||
data["LongitudeFixed"] = longitudeFixed;
|
||||
|
||||
data["MorningBeginFixed"] = morningBeginFixed.toString(Qt::ISODate);
|
||||
data["EveningBeginFixed"] = eveningBeginFixed.toString(Qt::ISODate);
|
||||
data["TransitionTime"] = transitionTime;
|
||||
};
|
||||
|
||||
auto compareValues = [&activeExpect, &modeExpect, &nightTemperatureExpect,
|
||||
&latitudeFixedExpect, &longitudeFixedExpect,
|
||||
&morningBeginFixedExpect, &eveningBeginFixedExpect,
|
||||
&transitionTimeExpect](QHash<QString, QVariant> info) {
|
||||
QCOMPARE(info.value("Active").toBool(), activeExpect);
|
||||
QCOMPARE(info.value("Mode").toInt(), modeExpect);
|
||||
QCOMPARE(info.value("NightTemperature").toInt(), nightTemperatureExpect);
|
||||
QCOMPARE(info.value("LatitudeFixed").toDouble(), latitudeFixedExpect);
|
||||
QCOMPARE(info.value("LongitudeFixed").toDouble(), longitudeFixedExpect);
|
||||
QCOMPARE(info.value("MorningBeginFixed").toString(), morningBeginFixedExpect.toString(Qt::ISODate));
|
||||
QCOMPARE(info.value("EveningBeginFixed").toString(), eveningBeginFixedExpect.toString(Qt::ISODate));
|
||||
QCOMPARE(info.value("TransitionTime").toInt(), transitionTimeExpect);
|
||||
};
|
||||
|
||||
ColorCorrect::Manager *manager = kwinApp()->platform()->colorCorrectManager();
|
||||
|
||||
// test with default values
|
||||
setData();
|
||||
manager->changeConfiguration(data);
|
||||
compareValues(manager->info());
|
||||
|
||||
// set to test values
|
||||
active = activeReadIn;
|
||||
mode = modeReadIn;
|
||||
nightTemperature = nightTemperatureReadIn;
|
||||
latitudeFixed = latitudeFixedReadIn;
|
||||
longitudeFixed = longitudeFixedReadIn;
|
||||
morningBeginFixed = morBeginFixedReadIn;
|
||||
eveningBeginFixed = eveBeginFixedReadIn;
|
||||
transitionTime = transitionTimeReadIn;
|
||||
|
||||
if (successReadIn) {
|
||||
activeExpect = activeReadIn;
|
||||
modeExpect = modeReadIn;
|
||||
nightTemperatureExpect = nightTemperatureReadIn;
|
||||
latitudeFixedExpect = latitudeFixedReadIn;
|
||||
longitudeFixedExpect = longitudeFixedReadIn;
|
||||
morningBeginFixedExpect = morBeginFixedReadIn;
|
||||
eveningBeginFixedExpect = eveBeginFixedReadIn;
|
||||
transitionTimeExpect = transitionTimeReadIn;
|
||||
}
|
||||
|
||||
// test with test values
|
||||
setData();
|
||||
QCOMPARE(manager->changeConfiguration(data), successReadIn);
|
||||
compareValues(manager->info());
|
||||
}
|
||||
|
||||
void ColorCorrectNightColorTest::testAutoLocationUpdate()
|
||||
{
|
||||
ColorCorrect::Manager *manager = kwinApp()->platform()->colorCorrectManager();
|
||||
auto info = manager->info();
|
||||
QCOMPARE(info.value("LatitudeAuto").toDouble(), 0.);
|
||||
QCOMPARE(info.value("LongitudeAuto").toDouble(), 0.);
|
||||
|
||||
// wrong latitude value
|
||||
manager->autoLocationUpdate(91, 15);
|
||||
info = manager->info();
|
||||
QCOMPARE(info.value("LatitudeAuto").toDouble(), 0.);
|
||||
QCOMPARE(info.value("LongitudeAuto").toDouble(), 0.);
|
||||
|
||||
// wrong longitude value
|
||||
manager->autoLocationUpdate(50, -181);
|
||||
info = manager->info();
|
||||
QCOMPARE(info.value("LatitudeAuto").toDouble(), 0.);
|
||||
QCOMPARE(info.value("LongitudeAuto").toDouble(), 0.);
|
||||
|
||||
// change
|
||||
manager->autoLocationUpdate(50, -180);
|
||||
info = manager->info();
|
||||
QCOMPARE(info.value("LatitudeAuto").toDouble(), 50.);
|
||||
QCOMPARE(info.value("LongitudeAuto").toDouble(), -180.);
|
||||
|
||||
// small deviation only
|
||||
manager->autoLocationUpdate(51.5, -179.5);
|
||||
info = manager->info();
|
||||
QCOMPARE(info.value("LongitudeAuto").toDouble(), -180.);
|
||||
QCOMPARE(info.value("LatitudeAuto").toDouble(), 50.);
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(ColorCorrectNightColorTest)
|
||||
#include "colorcorrect_nightcolor_test.moc"
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile arg="true" />
|
||||
<group name="NightColor">
|
||||
<entry name="ActiveEnabled" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="Active" type="Bool">
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="ModeEnabled" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="Mode" type="Enum">
|
||||
<choices name="KWin::ColorCorrect::NightColorMode">
|
||||
<choice name="Automatic"/>
|
||||
<choice name="Location"/>
|
||||
<choice name="Times"/>
|
||||
</choices>
|
||||
<default>NightColorMode::Automatic</default>
|
||||
</entry>
|
||||
<entry name="NightTemperatureEnabled" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="NightTemperature" type="Int">
|
||||
<default>4500</default>
|
||||
</entry>
|
||||
<entry name="LatitudeAuto" type="Double">
|
||||
<default>0.</default>
|
||||
</entry>
|
||||
<entry name="LongitudeAuto" type="Double">
|
||||
<default>0.</default>
|
||||
</entry>
|
||||
<entry name="LocationEnabled" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="LatitudeFixed" type="Double">
|
||||
<default>0.</default>
|
||||
</entry>
|
||||
<entry name="LongitudeFixed" type="Double">
|
||||
<default>0.</default>
|
||||
</entry>
|
||||
<entry name="MorningBeginFixed" type="String">
|
||||
<default>"0600"</default>
|
||||
</entry>
|
||||
<entry name="EveningBeginFixed" type="String">
|
||||
<default>"1800"</default>
|
||||
</entry>
|
||||
<entry name="TransitionTime" type="Int">
|
||||
<default>30</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
|
@ -0,0 +1,8 @@
|
|||
File=colorcorrect_settings.kcfg
|
||||
NameSpace=KWin::ColorCorrect
|
||||
ClassName=Settings
|
||||
Singleton=true
|
||||
Mutators=true
|
||||
# manager.h is needed for NightColorMode
|
||||
IncludeFiles=colorcorrection/manager.h
|
||||
UseEnumTypes=true
|
|
@ -0,0 +1,54 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
|
||||
#include "colorcorrectdbusinterface.h"
|
||||
#include "colorcorrectadaptor.h"
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
namespace KWin {
|
||||
namespace ColorCorrect {
|
||||
|
||||
ColorCorrectDBusInterface::ColorCorrectDBusInterface(Manager *parent)
|
||||
: QObject(parent)
|
||||
, m_manager(parent)
|
||||
{
|
||||
connect(m_manager, &Manager::configChange, this, &ColorCorrectDBusInterface::nightColorConfigChanged);
|
||||
new ColorCorrectAdaptor(this);
|
||||
QDBusConnection::sessionBus().registerObject(QStringLiteral("/ColorCorrect"), this);
|
||||
}
|
||||
|
||||
QHash<QString, QVariant> ColorCorrectDBusInterface::nightColorInfo()
|
||||
{
|
||||
return m_manager->info();
|
||||
}
|
||||
|
||||
bool ColorCorrectDBusInterface::setNightColorConfig(QHash<QString, QVariant> data)
|
||||
{
|
||||
return m_manager->changeConfiguration(data);
|
||||
}
|
||||
|
||||
void ColorCorrectDBusInterface::nightColorAutoLocationUpdate(double latitude, double longitude)
|
||||
{
|
||||
m_manager->autoLocationUpdate(latitude, longitude);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef KWIN_NIGHTCOLOR_DBUS_INTERFACE_H
|
||||
#define KWIN_NIGHTCOLOR_DBUS_INTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
namespace ColorCorrect
|
||||
{
|
||||
|
||||
class Manager;
|
||||
|
||||
class ColorCorrectDBusInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.ColorCorrect")
|
||||
|
||||
public:
|
||||
explicit ColorCorrectDBusInterface(Manager *parent);
|
||||
virtual ~ColorCorrectDBusInterface() = default;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* @brief Gives information about the current state of Night Color.
|
||||
*
|
||||
* The returned variant hash has always the fields:
|
||||
* - ActiveEnabled
|
||||
* - Active
|
||||
* - Mode
|
||||
* - NightTemperatureEnabled
|
||||
* - NightTemperature
|
||||
* - Running
|
||||
* - CurrentColorTemperature
|
||||
* - LatitudeAuto
|
||||
* - LongitudeAuto
|
||||
* - LocationEnabled
|
||||
* - LatitudeFixed
|
||||
* - LongitudeFixed
|
||||
* - TimingsEnabled
|
||||
* - MorningBeginFixed
|
||||
* - EveningBeginFixed
|
||||
* - TransitionTime
|
||||
*
|
||||
* @return QHash<QString, QVariant>
|
||||
* @see nightColorConfigChange
|
||||
* @see signalNightColorConfigChange
|
||||
* @since 5.12
|
||||
**/
|
||||
QHash<QString, QVariant> nightColorInfo();
|
||||
/**
|
||||
* @brief Allows changing the Night Color configuration.
|
||||
*
|
||||
* The provided variant hash can have the following fields:
|
||||
* - Active
|
||||
* - Mode
|
||||
* - NightTemperature
|
||||
* - LatitudeAuto
|
||||
* - LongitudeAuto
|
||||
* - LatitudeFixed
|
||||
* - LongitudeFixed
|
||||
* - MorningBeginFixed
|
||||
* - EveningBeginFixed
|
||||
* - TransitionTime
|
||||
*
|
||||
* It returns true if the configuration change was succesful, otherwise false.
|
||||
* A change request for the location or timings needs to provide all relevant fields at the same time
|
||||
* to be succesful. Otherwise the whole change request will get ignored. A change request will be ignored
|
||||
* as a whole as well, if one of the provided informations has been sent in a wrong format.
|
||||
*
|
||||
* @return bool
|
||||
* @see nightColorInfo
|
||||
* @see signalNightColorConfigChange
|
||||
* @since 5.12
|
||||
**/
|
||||
bool setNightColorConfig(QHash<QString, QVariant> data);
|
||||
/**
|
||||
* @brief For receiving auto location updates, primarily through the KDE Daemon
|
||||
* @return void
|
||||
* @since 5.12
|
||||
**/
|
||||
void nightColorAutoLocationUpdate(double latitude, double longitude);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief Emits that the Night Color configuration has been changed.
|
||||
*
|
||||
* The provided variant hash provides the same fields as @link nightColorInfo
|
||||
*
|
||||
* @return void
|
||||
* @see nightColorInfo
|
||||
* @see nightColorConfigChange
|
||||
* @since 5.12
|
||||
**/
|
||||
void nightColorConfigChanged(QHash<QString, QVariant> data);
|
||||
|
||||
private:
|
||||
Manager *m_manager;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // KWIN_NIGHTCOLOR_DBUS_INTERFACE_H
|
|
@ -0,0 +1,288 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_NIGHTCOLOR_CONSTANTS_H
|
||||
#define KWIN_NIGHTCOLOR_CONSTANTS_H
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace ColorCorrect
|
||||
{
|
||||
|
||||
static const int MSC_DAY = 86400000;
|
||||
static const int MIN_TEMPERATURE = 1000;
|
||||
static const int NEUTRAL_TEMPERATURE = 6500;
|
||||
static const int DEFAULT_NIGHT_TEMPERATURE = 4500;
|
||||
static const int FALLBACK_SLOW_UPDATE_TIME = 1800000; /* 30 minutes */
|
||||
|
||||
/* Whitepoint values for temperatures at 100K intervals.
|
||||
* These will be interpolated for the actual temperature.
|
||||
* This table was provided by Ingo Thies, 2013.
|
||||
* See the following file for more information:
|
||||
* https://github.com/jonls/redshift/blob/master/README-colorramp
|
||||
*/
|
||||
static const float blackbodyColor[] = {
|
||||
1.00000000, 0.18172716, 0.00000000, /* 1000K */
|
||||
1.00000000, 0.25503671, 0.00000000, /* 1100K */
|
||||
1.00000000, 0.30942099, 0.00000000, /* 1200K */
|
||||
1.00000000, 0.35357379, 0.00000000, /* ... */
|
||||
1.00000000, 0.39091524, 0.00000000,
|
||||
1.00000000, 0.42322816, 0.00000000,
|
||||
1.00000000, 0.45159884, 0.00000000,
|
||||
1.00000000, 0.47675916, 0.00000000,
|
||||
1.00000000, 0.49923747, 0.00000000,
|
||||
1.00000000, 0.51943421, 0.00000000,
|
||||
1.00000000, 0.54360078, 0.08679949, /* 2000K */
|
||||
1.00000000, 0.56618736, 0.14065513,
|
||||
1.00000000, 0.58734976, 0.18362641,
|
||||
1.00000000, 0.60724493, 0.22137978,
|
||||
1.00000000, 0.62600248, 0.25591950,
|
||||
1.00000000, 0.64373109, 0.28819679,
|
||||
1.00000000, 0.66052319, 0.31873863,
|
||||
1.00000000, 0.67645822, 0.34786758,
|
||||
1.00000000, 0.69160518, 0.37579588,
|
||||
1.00000000, 0.70602449, 0.40267128,
|
||||
1.00000000, 0.71976951, 0.42860152, /* 3000K */
|
||||
1.00000000, 0.73288760, 0.45366838,
|
||||
1.00000000, 0.74542112, 0.47793608,
|
||||
1.00000000, 0.75740814, 0.50145662,
|
||||
1.00000000, 0.76888303, 0.52427322,
|
||||
1.00000000, 0.77987699, 0.54642268,
|
||||
1.00000000, 0.79041843, 0.56793692,
|
||||
1.00000000, 0.80053332, 0.58884417,
|
||||
1.00000000, 0.81024551, 0.60916971,
|
||||
1.00000000, 0.81957693, 0.62893653,
|
||||
1.00000000, 0.82854786, 0.64816570, /* 4000K */
|
||||
1.00000000, 0.83717703, 0.66687674,
|
||||
1.00000000, 0.84548188, 0.68508786,
|
||||
1.00000000, 0.85347859, 0.70281616,
|
||||
1.00000000, 0.86118227, 0.72007777,
|
||||
1.00000000, 0.86860704, 0.73688797, /* 4500K */
|
||||
1.00000000, 0.87576611, 0.75326132,
|
||||
1.00000000, 0.88267187, 0.76921169,
|
||||
1.00000000, 0.88933596, 0.78475236,
|
||||
1.00000000, 0.89576933, 0.79989606,
|
||||
1.00000000, 0.90198230, 0.81465502, /* 5000K */
|
||||
1.00000000, 0.90963069, 0.82838210,
|
||||
1.00000000, 0.91710889, 0.84190889,
|
||||
1.00000000, 0.92441842, 0.85523742,
|
||||
1.00000000, 0.93156127, 0.86836903,
|
||||
1.00000000, 0.93853986, 0.88130458,
|
||||
1.00000000, 0.94535695, 0.89404470,
|
||||
1.00000000, 0.95201559, 0.90658983,
|
||||
1.00000000, 0.95851906, 0.91894041,
|
||||
1.00000000, 0.96487079, 0.93109690,
|
||||
1.00000000, 0.97107439, 0.94305985, /* 6000K */
|
||||
1.00000000, 0.97713351, 0.95482993,
|
||||
1.00000000, 0.98305189, 0.96640795,
|
||||
1.00000000, 0.98883326, 0.97779486,
|
||||
1.00000000, 0.99448139, 0.98899179,
|
||||
1.00000000, 1.00000000, 1.00000000, /* 6500K */
|
||||
0.98947904, 0.99348723, 1.00000000,
|
||||
0.97940448, 0.98722715, 1.00000000,
|
||||
0.96975025, 0.98120637, 1.00000000,
|
||||
0.96049223, 0.97541240, 1.00000000,
|
||||
0.95160805, 0.96983355, 1.00000000, /* 7000K */
|
||||
0.94303638, 0.96443333, 1.00000000,
|
||||
0.93480451, 0.95923080, 1.00000000,
|
||||
0.92689056, 0.95421394, 1.00000000,
|
||||
0.91927697, 0.94937330, 1.00000000,
|
||||
0.91194747, 0.94470005, 1.00000000,
|
||||
0.90488690, 0.94018594, 1.00000000,
|
||||
0.89808115, 0.93582323, 1.00000000,
|
||||
0.89151710, 0.93160469, 1.00000000,
|
||||
0.88518247, 0.92752354, 1.00000000,
|
||||
0.87906581, 0.92357340, 1.00000000, /* 8000K */
|
||||
0.87315640, 0.91974827, 1.00000000,
|
||||
0.86744421, 0.91604254, 1.00000000,
|
||||
0.86191983, 0.91245088, 1.00000000,
|
||||
0.85657444, 0.90896831, 1.00000000,
|
||||
0.85139976, 0.90559011, 1.00000000,
|
||||
0.84638799, 0.90231183, 1.00000000,
|
||||
0.84153180, 0.89912926, 1.00000000,
|
||||
0.83682430, 0.89603843, 1.00000000,
|
||||
0.83225897, 0.89303558, 1.00000000,
|
||||
0.82782969, 0.89011714, 1.00000000, /* 9000K */
|
||||
0.82353066, 0.88727974, 1.00000000,
|
||||
0.81935641, 0.88452017, 1.00000000,
|
||||
0.81530175, 0.88183541, 1.00000000,
|
||||
0.81136180, 0.87922257, 1.00000000,
|
||||
0.80753191, 0.87667891, 1.00000000,
|
||||
0.80380769, 0.87420182, 1.00000000,
|
||||
0.80018497, 0.87178882, 1.00000000,
|
||||
0.79665980, 0.86943756, 1.00000000,
|
||||
0.79322843, 0.86714579, 1.00000000,
|
||||
0.78988728, 0.86491137, 1.00000000, /* 10000K */
|
||||
0.78663296, 0.86273225, 1.00000000,
|
||||
0.78346225, 0.86060650, 1.00000000,
|
||||
0.78037207, 0.85853224, 1.00000000,
|
||||
0.77735950, 0.85650771, 1.00000000,
|
||||
0.77442176, 0.85453121, 1.00000000,
|
||||
0.77155617, 0.85260112, 1.00000000,
|
||||
0.76876022, 0.85071588, 1.00000000,
|
||||
0.76603147, 0.84887402, 1.00000000,
|
||||
0.76336762, 0.84707411, 1.00000000,
|
||||
0.76076645, 0.84531479, 1.00000000, /* 11000K */
|
||||
0.75822586, 0.84359476, 1.00000000,
|
||||
0.75574383, 0.84191277, 1.00000000,
|
||||
0.75331843, 0.84026762, 1.00000000,
|
||||
0.75094780, 0.83865816, 1.00000000,
|
||||
0.74863017, 0.83708329, 1.00000000,
|
||||
0.74636386, 0.83554194, 1.00000000,
|
||||
0.74414722, 0.83403311, 1.00000000,
|
||||
0.74197871, 0.83255582, 1.00000000,
|
||||
0.73985682, 0.83110912, 1.00000000,
|
||||
0.73778012, 0.82969211, 1.00000000, /* 12000K */
|
||||
0.73574723, 0.82830393, 1.00000000,
|
||||
0.73375683, 0.82694373, 1.00000000,
|
||||
0.73180765, 0.82561071, 1.00000000,
|
||||
0.72989845, 0.82430410, 1.00000000,
|
||||
0.72802807, 0.82302316, 1.00000000,
|
||||
0.72619537, 0.82176715, 1.00000000,
|
||||
0.72439927, 0.82053539, 1.00000000,
|
||||
0.72263872, 0.81932722, 1.00000000,
|
||||
0.72091270, 0.81814197, 1.00000000,
|
||||
0.71922025, 0.81697905, 1.00000000, /* 13000K */
|
||||
0.71756043, 0.81583783, 1.00000000,
|
||||
0.71593234, 0.81471775, 1.00000000,
|
||||
0.71433510, 0.81361825, 1.00000000,
|
||||
0.71276788, 0.81253878, 1.00000000,
|
||||
0.71122987, 0.81147883, 1.00000000,
|
||||
0.70972029, 0.81043789, 1.00000000,
|
||||
0.70823838, 0.80941546, 1.00000000,
|
||||
0.70678342, 0.80841109, 1.00000000,
|
||||
0.70535469, 0.80742432, 1.00000000,
|
||||
0.70395153, 0.80645469, 1.00000000, /* 14000K */
|
||||
0.70257327, 0.80550180, 1.00000000,
|
||||
0.70121928, 0.80456522, 1.00000000,
|
||||
0.69988894, 0.80364455, 1.00000000,
|
||||
0.69858167, 0.80273941, 1.00000000,
|
||||
0.69729688, 0.80184943, 1.00000000,
|
||||
0.69603402, 0.80097423, 1.00000000,
|
||||
0.69479255, 0.80011347, 1.00000000,
|
||||
0.69357196, 0.79926681, 1.00000000,
|
||||
0.69237173, 0.79843391, 1.00000000,
|
||||
0.69119138, 0.79761446, 1.00000000, /* 15000K */
|
||||
0.69003044, 0.79680814, 1.00000000,
|
||||
0.68888844, 0.79601466, 1.00000000,
|
||||
0.68776494, 0.79523371, 1.00000000,
|
||||
0.68665951, 0.79446502, 1.00000000,
|
||||
0.68557173, 0.79370830, 1.00000000,
|
||||
0.68450119, 0.79296330, 1.00000000,
|
||||
0.68344751, 0.79222975, 1.00000000,
|
||||
0.68241029, 0.79150740, 1.00000000,
|
||||
0.68138918, 0.79079600, 1.00000000,
|
||||
0.68038380, 0.79009531, 1.00000000, /* 16000K */
|
||||
0.67939381, 0.78940511, 1.00000000,
|
||||
0.67841888, 0.78872517, 1.00000000,
|
||||
0.67745866, 0.78805526, 1.00000000,
|
||||
0.67651284, 0.78739518, 1.00000000,
|
||||
0.67558112, 0.78674472, 1.00000000,
|
||||
0.67466317, 0.78610368, 1.00000000,
|
||||
0.67375872, 0.78547186, 1.00000000,
|
||||
0.67286748, 0.78484907, 1.00000000,
|
||||
0.67198916, 0.78423512, 1.00000000,
|
||||
0.67112350, 0.78362984, 1.00000000, /* 17000K */
|
||||
0.67027024, 0.78303305, 1.00000000,
|
||||
0.66942911, 0.78244457, 1.00000000,
|
||||
0.66859988, 0.78186425, 1.00000000,
|
||||
0.66778228, 0.78129191, 1.00000000,
|
||||
0.66697610, 0.78072740, 1.00000000,
|
||||
0.66618110, 0.78017057, 1.00000000,
|
||||
0.66539706, 0.77962127, 1.00000000,
|
||||
0.66462376, 0.77907934, 1.00000000,
|
||||
0.66386098, 0.77854465, 1.00000000,
|
||||
0.66310852, 0.77801705, 1.00000000, /* 18000K */
|
||||
0.66236618, 0.77749642, 1.00000000,
|
||||
0.66163375, 0.77698261, 1.00000000,
|
||||
0.66091106, 0.77647551, 1.00000000,
|
||||
0.66019791, 0.77597498, 1.00000000,
|
||||
0.65949412, 0.77548090, 1.00000000,
|
||||
0.65879952, 0.77499315, 1.00000000,
|
||||
0.65811392, 0.77451161, 1.00000000,
|
||||
0.65743716, 0.77403618, 1.00000000,
|
||||
0.65676908, 0.77356673, 1.00000000,
|
||||
0.65610952, 0.77310316, 1.00000000, /* 19000K */
|
||||
0.65545831, 0.77264537, 1.00000000,
|
||||
0.65481530, 0.77219324, 1.00000000,
|
||||
0.65418036, 0.77174669, 1.00000000,
|
||||
0.65355332, 0.77130560, 1.00000000,
|
||||
0.65293404, 0.77086988, 1.00000000,
|
||||
0.65232240, 0.77043944, 1.00000000,
|
||||
0.65171824, 0.77001419, 1.00000000,
|
||||
0.65112144, 0.76959404, 1.00000000,
|
||||
0.65053187, 0.76917889, 1.00000000,
|
||||
0.64994941, 0.76876866, 1.00000000, /* 20000K */
|
||||
0.64937392, 0.76836326, 1.00000000,
|
||||
0.64880528, 0.76796263, 1.00000000,
|
||||
0.64824339, 0.76756666, 1.00000000,
|
||||
0.64768812, 0.76717529, 1.00000000,
|
||||
0.64713935, 0.76678844, 1.00000000,
|
||||
0.64659699, 0.76640603, 1.00000000,
|
||||
0.64606092, 0.76602798, 1.00000000,
|
||||
0.64553103, 0.76565424, 1.00000000,
|
||||
0.64500722, 0.76528472, 1.00000000,
|
||||
0.64448939, 0.76491935, 1.00000000, /* 21000K */
|
||||
0.64397745, 0.76455808, 1.00000000,
|
||||
0.64347129, 0.76420082, 1.00000000,
|
||||
0.64297081, 0.76384753, 1.00000000,
|
||||
0.64247594, 0.76349813, 1.00000000,
|
||||
0.64198657, 0.76315256, 1.00000000,
|
||||
0.64150261, 0.76281076, 1.00000000,
|
||||
0.64102399, 0.76247267, 1.00000000,
|
||||
0.64055061, 0.76213824, 1.00000000,
|
||||
0.64008239, 0.76180740, 1.00000000,
|
||||
0.63961926, 0.76148010, 1.00000000, /* 22000K */
|
||||
0.63916112, 0.76115628, 1.00000000,
|
||||
0.63870790, 0.76083590, 1.00000000,
|
||||
0.63825953, 0.76051890, 1.00000000,
|
||||
0.63781592, 0.76020522, 1.00000000,
|
||||
0.63737701, 0.75989482, 1.00000000,
|
||||
0.63694273, 0.75958764, 1.00000000,
|
||||
0.63651299, 0.75928365, 1.00000000,
|
||||
0.63608774, 0.75898278, 1.00000000,
|
||||
0.63566691, 0.75868499, 1.00000000,
|
||||
0.63525042, 0.75839025, 1.00000000, /* 23000K */
|
||||
0.63483822, 0.75809849, 1.00000000,
|
||||
0.63443023, 0.75780969, 1.00000000,
|
||||
0.63402641, 0.75752379, 1.00000000,
|
||||
0.63362667, 0.75724075, 1.00000000,
|
||||
0.63323097, 0.75696053, 1.00000000,
|
||||
0.63283925, 0.75668310, 1.00000000,
|
||||
0.63245144, 0.75640840, 1.00000000,
|
||||
0.63206749, 0.75613641, 1.00000000,
|
||||
0.63168735, 0.75586707, 1.00000000,
|
||||
0.63131096, 0.75560036, 1.00000000, /* 24000K */
|
||||
0.63093826, 0.75533624, 1.00000000,
|
||||
0.63056920, 0.75507467, 1.00000000,
|
||||
0.63020374, 0.75481562, 1.00000000,
|
||||
0.62984181, 0.75455904, 1.00000000,
|
||||
0.62948337, 0.75430491, 1.00000000,
|
||||
0.62912838, 0.75405319, 1.00000000,
|
||||
0.62877678, 0.75380385, 1.00000000,
|
||||
0.62842852, 0.75355685, 1.00000000,
|
||||
0.62808356, 0.75331217, 1.00000000,
|
||||
0.62774186, 0.75306977, 1.00000000, /* 25000K */
|
||||
0.62740336, 0.75282962, 1.00000000
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KWIN_NIGHTCOLOR_CONSTANTS_H
|
|
@ -0,0 +1,50 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_GAMMARAMP_H
|
||||
#define KWIN_GAMMARAMP_H
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
namespace ColorCorrect
|
||||
{
|
||||
|
||||
struct GammaRamp {
|
||||
GammaRamp(int _size) {
|
||||
size = _size;
|
||||
red = new uint16_t[3 * _size];
|
||||
green = red + _size;
|
||||
blue = green + _size;
|
||||
}
|
||||
~GammaRamp() {
|
||||
delete[] red;
|
||||
red = green = blue = nullptr;
|
||||
}
|
||||
|
||||
uint32_t size = 0;
|
||||
uint16_t *red = nullptr;
|
||||
uint16_t *green = nullptr;
|
||||
uint16_t *blue = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KWIN_GAMMARAMP_H
|
|
@ -0,0 +1,766 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#include "manager.h"
|
||||
#include "colorcorrectdbusinterface.h"
|
||||
#include "suncalc.h"
|
||||
#include "gammaramp.h"
|
||||
#include <colorcorrect_logging.h>
|
||||
|
||||
#include <main.h>
|
||||
#include <platform.h>
|
||||
#include <screens.h>
|
||||
#include <workspace.h>
|
||||
#include <logind.h>
|
||||
|
||||
#include <colorcorrect_settings.h>
|
||||
|
||||
#include <QTimer>
|
||||
#include <QDBusConnection>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace KWin {
|
||||
namespace ColorCorrect {
|
||||
|
||||
static const int QUICK_ADJUST_DURATION = 2000;
|
||||
static const int TEMPERATURE_STEP = 50;
|
||||
|
||||
static bool checkLocation(double lat, double lng)
|
||||
{
|
||||
return -90 <= lat && lat <= 90 && -180 <= lng && lng <= 180;
|
||||
}
|
||||
|
||||
Manager::Manager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_iface = new ColorCorrectDBusInterface(this);
|
||||
connect(kwinApp(), &Application::workspaceCreated, this, &Manager::init);
|
||||
}
|
||||
|
||||
void Manager::init()
|
||||
{
|
||||
Settings::instance(kwinApp()->config());
|
||||
// we may always read in the current config
|
||||
readConfig();
|
||||
|
||||
if (!kwinApp()->platform()->supportsGammaControl()) {
|
||||
// at least update the sun timings to make the values accessible via dbus
|
||||
updateSunTimings(true);
|
||||
return;
|
||||
}
|
||||
|
||||
connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this,
|
||||
[this](bool active) {
|
||||
if (active) {
|
||||
hardReset();
|
||||
} else {
|
||||
cancelAllTimers();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// monitor for system clock changes - from the time dataengine
|
||||
auto timeChangedFd = ::timerfd_create(CLOCK_REALTIME, O_CLOEXEC | O_NONBLOCK);
|
||||
::itimerspec timespec;
|
||||
//set all timers to 0, which creates a timer that won't do anything
|
||||
::memset(×pec, 0, sizeof(timespec));
|
||||
|
||||
// Monitor for the time changing (flags == TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET).
|
||||
// However these are not exposed in glibc so value is hardcoded:
|
||||
::timerfd_settime(timeChangedFd, 3, ×pec, 0);
|
||||
|
||||
connect(this, &QObject::destroyed, [timeChangedFd]() {
|
||||
::close(timeChangedFd);
|
||||
});
|
||||
|
||||
auto notifier = new QSocketNotifier(timeChangedFd, QSocketNotifier::Read, this);
|
||||
connect(notifier, &QSocketNotifier::activated, this, [this](int fd) {
|
||||
uint64_t c;
|
||||
::read(fd, &c, 8);
|
||||
|
||||
// check if we're resuming from suspend - in this case do a hard reset
|
||||
// Note: We're using the time clock to detect a suspend phase instead of connecting to the
|
||||
// provided logind dbus signal, because this signal would be received way too late.
|
||||
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
QStringLiteral("Get"));
|
||||
message.setArguments(QVariantList({"org.freedesktop.login1.Manager", QStringLiteral("PreparingForSleep")}));
|
||||
QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(message);
|
||||
bool comingFromSuspend;
|
||||
if (reply.isValid()) {
|
||||
comingFromSuspend = reply.value().toBool();
|
||||
} else {
|
||||
qCDebug(KWIN_COLORCORRECTION) << "Failed to get PreparingForSleep Property of logind session:" << reply.error().message();
|
||||
// Always do a hard reset in case we have no further information.
|
||||
comingFromSuspend = true;
|
||||
}
|
||||
|
||||
if (comingFromSuspend) {
|
||||
hardReset();
|
||||
} else {
|
||||
resetAllTimers();
|
||||
}
|
||||
});
|
||||
#else
|
||||
// TODO: Alternative method for BSD.
|
||||
#endif
|
||||
|
||||
hardReset();
|
||||
}
|
||||
|
||||
void Manager::hardReset()
|
||||
{
|
||||
cancelAllTimers();
|
||||
updateSunTimings(true);
|
||||
if (kwinApp()->platform()->supportsGammaControl() && m_active) {
|
||||
m_running = true;
|
||||
commitGammaRamps(currentTargetTemp());
|
||||
}
|
||||
resetAllTimers();
|
||||
}
|
||||
|
||||
void Manager::reparseConfigAndReset()
|
||||
{
|
||||
cancelAllTimers();
|
||||
readConfig();
|
||||
hardReset();
|
||||
}
|
||||
|
||||
void Manager::readConfig()
|
||||
{
|
||||
Settings *s = Settings::self();
|
||||
s->load();
|
||||
|
||||
m_active = s->active();
|
||||
m_mode = s->mode();
|
||||
m_nightTargetTemp = qBound(MIN_TEMPERATURE, s->nightTemperature(), NEUTRAL_TEMPERATURE);
|
||||
|
||||
double lat, lng;
|
||||
auto correctReadin = [&lat, &lng]() {
|
||||
if (!checkLocation(lat, lng)) {
|
||||
// out of domain
|
||||
lat = 0;
|
||||
lng = 0;
|
||||
}
|
||||
};
|
||||
// automatic
|
||||
lat = s->latitudeAuto();
|
||||
lng = s->longitudeAuto();
|
||||
correctReadin();
|
||||
m_latAuto = lat;
|
||||
m_lngAuto = lng;
|
||||
// fixed location
|
||||
lat = s->latitudeFixed();
|
||||
lng = s->longitudeFixed();
|
||||
correctReadin();
|
||||
m_latFixed = lat;
|
||||
m_lngFixed = lng;
|
||||
|
||||
// fixed timings
|
||||
QTime mrB = QTime::fromString(s->morningBeginFixed(), "hhmm");
|
||||
QTime evB = QTime::fromString(s->eveningBeginFixed(), "hhmm");
|
||||
|
||||
int diffME = mrB.msecsTo(evB);
|
||||
if (diffME <= 0) {
|
||||
// morning not strictly before evening - use defaults
|
||||
mrB = QTime(6,0);
|
||||
evB = QTime(18,0);
|
||||
diffME = mrB.msecsTo(evB);
|
||||
}
|
||||
int diffMin = qMin(diffME, MSC_DAY - diffME);
|
||||
|
||||
int trTime = s->transitionTime() * 1000 * 60;
|
||||
if (trTime < 0 || diffMin <= trTime) {
|
||||
// transition time too long - use defaults
|
||||
mrB = QTime(6,0);
|
||||
evB = QTime(18,0);
|
||||
trTime = FALLBACK_SLOW_UPDATE_TIME;
|
||||
}
|
||||
m_morning = mrB;
|
||||
m_evening = evB;
|
||||
m_trTime = qMax(trTime / 1000 / 60, 1);
|
||||
}
|
||||
|
||||
void Manager::resetAllTimers()
|
||||
{
|
||||
cancelAllTimers();
|
||||
if (kwinApp()->platform()->supportsGammaControl()) {
|
||||
if (m_active) {
|
||||
m_running = true;
|
||||
}
|
||||
// we do this also for active being false in order to reset the temperature back to the day value
|
||||
resetQuickAdjustTimer();
|
||||
} else {
|
||||
m_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::cancelAllTimers()
|
||||
{
|
||||
delete m_slowUpdateStartTimer;
|
||||
delete m_slowUpdateTimer;
|
||||
delete m_quickAdjustTimer;
|
||||
|
||||
m_slowUpdateStartTimer = nullptr;
|
||||
m_slowUpdateTimer = nullptr;
|
||||
m_quickAdjustTimer = nullptr;
|
||||
}
|
||||
|
||||
void Manager::resetQuickAdjustTimer()
|
||||
{
|
||||
updateSunTimings(false);
|
||||
|
||||
int tempDiff = qAbs(currentTargetTemp() - m_currentTemp);
|
||||
// allow tolerance of one TEMPERATURE_STEP to compensate if a slow update is coincidental
|
||||
if (tempDiff > TEMPERATURE_STEP) {
|
||||
cancelAllTimers();
|
||||
m_quickAdjustTimer = new QTimer(this);
|
||||
m_quickAdjustTimer->setSingleShot(false);
|
||||
connect(m_quickAdjustTimer, &QTimer::timeout, this, &Manager::quickAdjust);
|
||||
|
||||
int interval = QUICK_ADJUST_DURATION / (tempDiff / TEMPERATURE_STEP);
|
||||
if (interval == 0) {
|
||||
interval = 1;
|
||||
}
|
||||
m_quickAdjustTimer->start(interval);
|
||||
} else {
|
||||
resetSlowUpdateStartTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::quickAdjust()
|
||||
{
|
||||
if (!m_quickAdjustTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
int nextTemp;
|
||||
int targetTemp = currentTargetTemp();
|
||||
|
||||
if (m_currentTemp < targetTemp) {
|
||||
nextTemp = qMin(m_currentTemp + TEMPERATURE_STEP, targetTemp);
|
||||
} else {
|
||||
nextTemp = qMax(m_currentTemp - TEMPERATURE_STEP, targetTemp);
|
||||
}
|
||||
commitGammaRamps(nextTemp);
|
||||
|
||||
if (nextTemp == targetTemp) {
|
||||
// stop timer, we reached the target temp
|
||||
delete m_quickAdjustTimer;
|
||||
m_quickAdjustTimer = nullptr;
|
||||
resetSlowUpdateStartTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::resetSlowUpdateStartTimer()
|
||||
{
|
||||
delete m_slowUpdateStartTimer;
|
||||
m_slowUpdateStartTimer = nullptr;
|
||||
|
||||
if (!m_running || m_quickAdjustTimer) {
|
||||
// only reenable the slow update start timer when quick adjust is not active anymore
|
||||
return;
|
||||
}
|
||||
|
||||
// set up the next slow update
|
||||
m_slowUpdateStartTimer = new QTimer(this);
|
||||
m_slowUpdateStartTimer->setSingleShot(true);
|
||||
connect(m_slowUpdateStartTimer, &QTimer::timeout, this, &Manager::resetSlowUpdateStartTimer);
|
||||
|
||||
updateSunTimings(false);
|
||||
int diff;
|
||||
if (m_mode == NightColorMode::Timings) {
|
||||
// Timings mode is in local time
|
||||
diff = QDateTime::currentDateTime().msecsTo(m_next.first);
|
||||
} else {
|
||||
diff = QDateTime::currentDateTimeUtc().msecsTo(m_next.first);
|
||||
}
|
||||
if (diff <= 0) {
|
||||
qCCritical(KWIN_COLORCORRECTION) << "Error in time calculation. Deactivating Night Color.";
|
||||
return;
|
||||
}
|
||||
m_slowUpdateStartTimer->start(diff);
|
||||
|
||||
// start the current slow update
|
||||
resetSlowUpdateTimer();
|
||||
}
|
||||
|
||||
void Manager::resetSlowUpdateTimer()
|
||||
{
|
||||
delete m_slowUpdateTimer;
|
||||
m_slowUpdateTimer = nullptr;
|
||||
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
bool isDay = daylight();
|
||||
int targetTemp = isDay ? m_dayTargetTemp : m_nightTargetTemp;
|
||||
|
||||
if (m_prev.first == m_prev.second) {
|
||||
// transition time is zero
|
||||
commitGammaRamps(isDay ? m_dayTargetTemp : m_nightTargetTemp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_prev.first <= now && now <= m_prev.second) {
|
||||
int availTime = now.msecsTo(m_prev.second);
|
||||
m_slowUpdateTimer = new QTimer(this);
|
||||
m_slowUpdateTimer->setSingleShot(false);
|
||||
if (isDay) {
|
||||
connect(m_slowUpdateTimer, &QTimer::timeout, this, [this]() {slowUpdate(m_dayTargetTemp);});
|
||||
} else {
|
||||
connect(m_slowUpdateTimer, &QTimer::timeout, this, [this]() {slowUpdate(m_nightTargetTemp);});
|
||||
}
|
||||
|
||||
// calculate interval such as temperature is changed by TEMPERATURE_STEP K per timer timeout
|
||||
int interval = availTime / (qAbs(targetTemp - m_currentTemp) / TEMPERATURE_STEP);
|
||||
if (interval == 0) {
|
||||
interval = 1;
|
||||
}
|
||||
m_slowUpdateTimer->start(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::slowUpdate(int targetTemp)
|
||||
{
|
||||
if (!m_slowUpdateTimer) {
|
||||
return;
|
||||
}
|
||||
int nextTemp;
|
||||
if (m_currentTemp < targetTemp) {
|
||||
nextTemp = qMin(m_currentTemp + TEMPERATURE_STEP, targetTemp);
|
||||
} else {
|
||||
nextTemp = qMax(m_currentTemp - TEMPERATURE_STEP, targetTemp);
|
||||
}
|
||||
commitGammaRamps(nextTemp);
|
||||
if (nextTemp == targetTemp) {
|
||||
// stop timer, we reached the target temp
|
||||
delete m_slowUpdateTimer;
|
||||
m_slowUpdateTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::updateSunTimings(bool force)
|
||||
{
|
||||
QDateTime todayNow = QDateTime::currentDateTimeUtc();
|
||||
|
||||
if (m_mode == NightColorMode::Timings) {
|
||||
|
||||
QDateTime todayNowLocal = QDateTime::currentDateTime();
|
||||
|
||||
QDateTime morB = QDateTime(todayNowLocal.date(), m_morning);
|
||||
QDateTime morE = morB.addSecs(m_trTime * 60);
|
||||
QDateTime eveB = QDateTime(todayNowLocal.date(), m_evening);
|
||||
QDateTime eveE = eveB.addSecs(m_trTime * 60);
|
||||
|
||||
if (morB <= todayNowLocal && todayNowLocal < eveB) {
|
||||
m_next = DateTimes(eveB, eveE);
|
||||
m_prev = DateTimes(morB, morE);
|
||||
} else if (todayNowLocal < morB) {
|
||||
m_next = DateTimes(morB, morE);
|
||||
m_prev = DateTimes(eveB.addDays(-1), eveE.addDays(-1));
|
||||
} else {
|
||||
m_next = DateTimes(morB.addDays(1), morE.addDays(1));
|
||||
m_prev = DateTimes(eveB, eveE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double lat, lng;
|
||||
if (m_mode == NightColorMode::Automatic) {
|
||||
lat = m_latAuto;
|
||||
lng = m_lngAuto;
|
||||
} else {
|
||||
lat = m_latFixed;
|
||||
lng = m_lngFixed;
|
||||
}
|
||||
|
||||
if (!force) {
|
||||
// first try by only switching the timings
|
||||
if (daylight()) {
|
||||
// next is morning
|
||||
m_prev = m_next;
|
||||
m_next = getSunTimings(todayNow.date().addDays(1), lat, lng, true);
|
||||
} else {
|
||||
// next is evening
|
||||
m_prev = m_next;
|
||||
m_next = getSunTimings(todayNow.date(), lat, lng, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (force || !checkAutomaticSunTimings()) {
|
||||
// in case this fails, reset them
|
||||
DateTimes morning = getSunTimings(todayNow.date(), lat, lng, true);
|
||||
if (todayNow < morning.first) {
|
||||
m_prev = getSunTimings(todayNow.date().addDays(-1), lat, lng, false);
|
||||
m_next = morning;
|
||||
} else {
|
||||
DateTimes evening = getSunTimings(todayNow.date(), lat, lng, false);
|
||||
if (todayNow < evening.first) {
|
||||
m_prev = morning;
|
||||
m_next = evening;
|
||||
} else {
|
||||
m_prev = evening;
|
||||
m_next = getSunTimings(todayNow.date().addDays(1), lat, lng, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DateTimes Manager::getSunTimings(QDate date, double latitude, double longitude, bool morning) const
|
||||
{
|
||||
Times times = calculateSunTimings(date, latitude, longitude, morning);
|
||||
// At locations near the poles it is possible, that we can't
|
||||
// calculate some or all sun timings (midnight sun).
|
||||
// In this case try to fallback to sensible default values.
|
||||
bool beginDefined = !times.first.isNull();
|
||||
bool endDefined = !times.second.isNull();
|
||||
if (!beginDefined || !endDefined) {
|
||||
if (beginDefined) {
|
||||
times.second = times.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME );
|
||||
} else if (endDefined) {
|
||||
times.first = times.second.addMSecs( - FALLBACK_SLOW_UPDATE_TIME);
|
||||
} else {
|
||||
// Just use default values for morning and evening, but the user
|
||||
// will probably deactivate Night Color anyway if he is living
|
||||
// in a region without clear sun rise and set.
|
||||
times.first = morning ? QTime(6,0,0) : QTime(18,0,0);
|
||||
times.second = times.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME );
|
||||
}
|
||||
}
|
||||
return DateTimes(QDateTime(date, times.first, Qt::UTC), QDateTime(date, times.second, Qt::UTC));
|
||||
}
|
||||
|
||||
bool Manager::checkAutomaticSunTimings() const
|
||||
{
|
||||
if (m_prev.first.isValid() && m_prev.second.isValid() &&
|
||||
m_next.first.isValid() && m_next.second.isValid()) {
|
||||
QDateTime todayNow = QDateTime::currentDateTimeUtc();
|
||||
return m_prev.first <= todayNow && todayNow < m_next.first &&
|
||||
m_prev.first.msecsTo(m_next.first) < MSC_DAY * 23./24;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Manager::daylight() const
|
||||
{
|
||||
return m_prev.first.date() == m_next.first.date();
|
||||
}
|
||||
|
||||
int Manager::currentTargetTemp() const
|
||||
{
|
||||
if (!m_active) {
|
||||
return NEUTRAL_TEMPERATURE;
|
||||
}
|
||||
|
||||
QDateTime todayNow = QDateTime::currentDateTimeUtc();
|
||||
|
||||
auto f = [this, todayNow](int target1, int target2) {
|
||||
if (todayNow <= m_prev.second) {
|
||||
double residueQuota = todayNow.msecsTo(m_prev.second) / (double)m_prev.first.msecsTo(m_prev.second);
|
||||
|
||||
double ret = (int)((1. - residueQuota) * (double)target2 + residueQuota * (double)target1);
|
||||
// remove single digits
|
||||
ret = ((int)(0.1 * ret)) * 10;
|
||||
return (int)ret;
|
||||
} else {
|
||||
return target2;
|
||||
}
|
||||
};
|
||||
|
||||
if (daylight()) {
|
||||
return f(m_nightTargetTemp, m_dayTargetTemp);
|
||||
} else {
|
||||
return f(m_dayTargetTemp, m_nightTargetTemp);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::commitGammaRamps(int temperature)
|
||||
{
|
||||
int nscreens = Screens::self()->count();
|
||||
|
||||
for (int screen = 0; screen < nscreens; screen++) {
|
||||
int rampsize = kwinApp()->platform()->gammaRampSize(screen);
|
||||
GammaRamp ramp(rampsize);
|
||||
|
||||
/*
|
||||
* The gamma calculation below is based on the Redshift app:
|
||||
* https://github.com/jonls/redshift
|
||||
*/
|
||||
|
||||
// linear default state
|
||||
for (int i = 0; i < rampsize; i++) {
|
||||
uint16_t value = (double)i / rampsize * (UINT16_MAX + 1);
|
||||
ramp.red[i] = value;
|
||||
ramp.green[i] = value;
|
||||
ramp.blue[i] = value;
|
||||
}
|
||||
|
||||
// approximate white point
|
||||
float whitePoint[3];
|
||||
float alpha = (temperature % 100) / 100.;
|
||||
int bbCIndex = ((temperature - 1000) / 100) * 3;
|
||||
whitePoint[0] = (1. - alpha) * blackbodyColor[bbCIndex] + alpha * blackbodyColor[bbCIndex + 3];
|
||||
whitePoint[1] = (1. - alpha) * blackbodyColor[bbCIndex + 1] + alpha * blackbodyColor[bbCIndex + 4];
|
||||
whitePoint[2] = (1. - alpha) * blackbodyColor[bbCIndex + 2] + alpha * blackbodyColor[bbCIndex + 5];
|
||||
|
||||
for (int i = 0; i < rampsize; i++) {
|
||||
ramp.red[i] = (double)ramp.red[i] / (UINT16_MAX+1) * whitePoint[0] * (UINT16_MAX+1);
|
||||
ramp.green[i] = (double)ramp.green[i] / (UINT16_MAX+1) * whitePoint[1] * (UINT16_MAX+1);
|
||||
ramp.blue[i] = (double)ramp.blue[i] / (UINT16_MAX+1) * whitePoint[2] * (UINT16_MAX+1);
|
||||
}
|
||||
|
||||
if (kwinApp()->platform()->setGammaRamp(screen, ramp)) {
|
||||
m_currentTemp = temperature;
|
||||
m_failedCommitAttempts = 0;
|
||||
} else {
|
||||
m_failedCommitAttempts++;
|
||||
if (m_failedCommitAttempts < 10) {
|
||||
qCWarning(KWIN_COLORCORRECTION).nospace() << "Committing Gamma Ramp failed for screen " << screen <<
|
||||
". Trying " << (10 - m_failedCommitAttempts) << " times more.";
|
||||
} else {
|
||||
// TODO: On multi monitor setups we could try to rollback earlier changes for already commited outputs
|
||||
qCWarning(KWIN_COLORCORRECTION) << "Gamma Ramp commit failed too often. Deactivating color correction for now.";
|
||||
m_failedCommitAttempts = 0; // reset so we can try again later (i.e. after suspend phase or config change)
|
||||
m_running = false;
|
||||
cancelAllTimers();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHash<QString, QVariant> Manager::info() const
|
||||
{
|
||||
return QHash<QString, QVariant> {
|
||||
{ QStringLiteral("Available"), kwinApp()->platform()->supportsGammaControl() },
|
||||
|
||||
{ QStringLiteral("ActiveEnabled"), true},
|
||||
{ QStringLiteral("Active"), m_active},
|
||||
|
||||
{ QStringLiteral("ModeEnabled"), true},
|
||||
{ QStringLiteral("Mode"), (int)m_mode},
|
||||
|
||||
{ QStringLiteral("NightTemperatureEnabled"), true},
|
||||
{ QStringLiteral("NightTemperature"), m_nightTargetTemp},
|
||||
|
||||
{ QStringLiteral("Running"), m_running},
|
||||
{ QStringLiteral("CurrentColorTemperature"), m_currentTemp},
|
||||
|
||||
{ QStringLiteral("LatitudeAuto"), m_latAuto},
|
||||
{ QStringLiteral("LongitudeAuto"), m_lngAuto},
|
||||
|
||||
{ QStringLiteral("LocationEnabled"), true},
|
||||
{ QStringLiteral("LatitudeFixed"), m_latFixed},
|
||||
{ QStringLiteral("LongitudeFixed"), m_lngFixed},
|
||||
|
||||
{ QStringLiteral("TimingsEnabled"), true},
|
||||
{ QStringLiteral("MorningBeginFixed"), m_morning.toString(Qt::ISODate)},
|
||||
{ QStringLiteral("EveningBeginFixed"), m_evening.toString(Qt::ISODate)},
|
||||
{ QStringLiteral("TransitionTime"), m_trTime},
|
||||
};
|
||||
}
|
||||
|
||||
bool Manager::changeConfiguration(QHash<QString, QVariant> data)
|
||||
{
|
||||
bool activeUpdate, modeUpdate, tempUpdate, locUpdate, timeUpdate;
|
||||
activeUpdate = modeUpdate = tempUpdate = locUpdate = timeUpdate = false;
|
||||
|
||||
bool active = m_active;
|
||||
NightColorMode mode = m_mode;
|
||||
int nightT = m_nightTargetTemp;
|
||||
|
||||
double lat = m_latFixed;
|
||||
double lng = m_lngFixed;
|
||||
|
||||
QTime mor = m_morning;
|
||||
QTime eve = m_evening;
|
||||
int trT = m_trTime;
|
||||
|
||||
QHash<QString, QVariant>::const_iterator iter1, iter2, iter3;
|
||||
|
||||
iter1 = data.constFind("Active");
|
||||
if (iter1 != data.constEnd()) {
|
||||
if (!iter1.value().canConvert<bool>()) {
|
||||
return false;
|
||||
}
|
||||
bool act = iter1.value().toBool();
|
||||
activeUpdate = m_active != act;
|
||||
active = act;
|
||||
}
|
||||
|
||||
iter1 = data.constFind("Mode");
|
||||
if (iter1 != data.constEnd()) {
|
||||
if (!iter1.value().canConvert<int>()) {
|
||||
return false;
|
||||
}
|
||||
int mo = iter1.value().toInt();
|
||||
if (mo < 0 || 2 < mo) {
|
||||
return false;
|
||||
}
|
||||
NightColorMode moM;
|
||||
switch (mo) {
|
||||
case 0:
|
||||
moM = NightColorMode::Automatic;
|
||||
break;
|
||||
case 1:
|
||||
moM = NightColorMode::Location;
|
||||
break;
|
||||
case 2:
|
||||
moM = NightColorMode::Timings;
|
||||
}
|
||||
modeUpdate = m_mode != moM;
|
||||
mode = moM;
|
||||
}
|
||||
|
||||
iter1 = data.constFind("NightTemperature");
|
||||
if (iter1 != data.constEnd()) {
|
||||
if (!iter1.value().canConvert<int>()) {
|
||||
return false;
|
||||
}
|
||||
int nT = iter1.value().toInt();
|
||||
if (nT < MIN_TEMPERATURE || NEUTRAL_TEMPERATURE < nT) {
|
||||
return false;
|
||||
}
|
||||
tempUpdate = m_nightTargetTemp != nT;
|
||||
nightT = nT;
|
||||
}
|
||||
|
||||
iter1 = data.constFind("LatitudeFixed");
|
||||
iter2 = data.constFind("LongitudeFixed");
|
||||
if (iter1 != data.constEnd() && iter2 != data.constEnd()) {
|
||||
if (!iter1.value().canConvert<double>() || !iter2.value().canConvert<double>()) {
|
||||
return false;
|
||||
}
|
||||
double la = iter1.value().toDouble();
|
||||
double ln = iter2.value().toDouble();
|
||||
if (!checkLocation(la, ln)) {
|
||||
return false;
|
||||
}
|
||||
locUpdate = m_latFixed != la || m_lngFixed != ln;
|
||||
lat = la;
|
||||
lng = ln;
|
||||
}
|
||||
|
||||
iter1 = data.constFind("MorningBeginFixed");
|
||||
iter2 = data.constFind("EveningBeginFixed");
|
||||
iter3 = data.constFind("TransitionTime");
|
||||
if (iter1 != data.constEnd() && iter2 != data.constEnd() && iter3 != data.constEnd()) {
|
||||
if (!iter1.value().canConvert<QString>() || !iter2.value().canConvert<QString>() || !iter3.value().canConvert<int>()) {
|
||||
return false;
|
||||
}
|
||||
QTime mo = QTime::fromString(iter1.value().toString(), Qt::ISODate);
|
||||
QTime ev = QTime::fromString(iter2.value().toString(), Qt::ISODate);
|
||||
if (!mo.isValid() || !ev.isValid()) {
|
||||
return false;
|
||||
}
|
||||
int tT = iter3.value().toInt();
|
||||
|
||||
int diffME = mo.msecsTo(ev);
|
||||
if (diffME <= 0 || qMin(diffME, MSC_DAY - diffME) <= tT * 60 * 1000 || tT < 1) {
|
||||
// morning not strictly before evening, transition time too long or transition time out of bounds
|
||||
return false;
|
||||
}
|
||||
|
||||
timeUpdate = m_morning != mo || m_evening != ev || m_trTime != tT;
|
||||
mor = mo;
|
||||
eve = ev;
|
||||
trT = tT;
|
||||
}
|
||||
|
||||
if (!(activeUpdate || modeUpdate || tempUpdate || locUpdate || timeUpdate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resetNeeded = activeUpdate || modeUpdate || tempUpdate ||
|
||||
(locUpdate && mode == NightColorMode::Location) ||
|
||||
(timeUpdate && mode == NightColorMode::Timings);
|
||||
|
||||
if (resetNeeded) {
|
||||
cancelAllTimers();
|
||||
}
|
||||
|
||||
Settings *s = Settings::self();
|
||||
if (activeUpdate) {
|
||||
m_active = active;
|
||||
s->setActive(active);
|
||||
}
|
||||
|
||||
if (modeUpdate) {
|
||||
m_mode = mode;
|
||||
s->setMode(mode);
|
||||
}
|
||||
|
||||
if (tempUpdate) {
|
||||
m_nightTargetTemp = nightT;
|
||||
s->setNightTemperature(nightT);
|
||||
}
|
||||
|
||||
if (locUpdate) {
|
||||
m_latFixed = lat;
|
||||
m_lngFixed = lng;
|
||||
s->setLatitudeFixed(lat);
|
||||
s->setLongitudeFixed(lng);
|
||||
}
|
||||
|
||||
if (timeUpdate) {
|
||||
m_morning = mor;
|
||||
m_evening = eve;
|
||||
m_trTime = trT;
|
||||
s->setMorningBeginFixed(mor.toString("hhmm"));
|
||||
s->setEveningBeginFixed(eve.toString("hhmm"));
|
||||
s->setTransitionTime(trT);
|
||||
}
|
||||
s->save();
|
||||
|
||||
if (resetNeeded) {
|
||||
resetAllTimers();
|
||||
}
|
||||
emit configChange(info());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Manager::autoLocationUpdate(double latitude, double longitude)
|
||||
{
|
||||
if (!checkLocation(latitude, longitude)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we tolerate small deviations with minimal impact on sun timings
|
||||
if (qAbs(m_latAuto - latitude) < 2 && qAbs(m_lngAuto - longitude) < 1) {
|
||||
return;
|
||||
}
|
||||
cancelAllTimers();
|
||||
m_latAuto = latitude;
|
||||
m_lngAuto = longitude;
|
||||
|
||||
Settings *s = Settings::self();
|
||||
s->setLatitudeAuto(latitude);
|
||||
s->setLongitudeAuto(longitude);
|
||||
s->save();
|
||||
|
||||
resetAllTimers();
|
||||
emit configChange(info());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_COLORCORRECT_MANAGER_H
|
||||
#define KWIN_COLORCORRECT_MANAGER_H
|
||||
|
||||
#include "constants.h"
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QDateTime>
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Platform;
|
||||
|
||||
namespace ColorCorrect
|
||||
{
|
||||
|
||||
typedef QPair<QDateTime,QDateTime> DateTimes;
|
||||
typedef QPair<QTime,QTime> Times;
|
||||
|
||||
class ColorCorrectDBusInterface;
|
||||
|
||||
|
||||
enum NightColorMode {
|
||||
// timings are based on provided location data
|
||||
Automatic = 0,
|
||||
// timings are based on fixed location data
|
||||
Location,
|
||||
// fixed timings
|
||||
Timings
|
||||
};
|
||||
|
||||
class KWIN_EXPORT Manager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Manager(QObject *parent);
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Get current configuration
|
||||
* @see changeConfiguration
|
||||
* @since 5.12
|
||||
**/
|
||||
QHash<QString, QVariant> info() const;
|
||||
/**
|
||||
* Change configuration
|
||||
* @see info
|
||||
* @since 5.12
|
||||
**/
|
||||
bool changeConfiguration(QHash<QString, QVariant> data);
|
||||
void autoLocationUpdate(double latitude, double longitude);
|
||||
|
||||
// for auto tests
|
||||
void reparseConfigAndReset();
|
||||
|
||||
public Q_SLOTS:
|
||||
void resetSlowUpdateStartTimer();
|
||||
void quickAdjust();
|
||||
|
||||
Q_SIGNALS:
|
||||
void configChange(QHash<QString, QVariant> data);
|
||||
|
||||
private:
|
||||
void readConfig();
|
||||
void hardReset();
|
||||
void slowUpdate(int targetTemp);
|
||||
void resetAllTimers();
|
||||
int currentTargetTemp() const;
|
||||
void cancelAllTimers();
|
||||
/**
|
||||
* Quick shift on manual change to current target Temperature
|
||||
**/
|
||||
void resetQuickAdjustTimer();
|
||||
/**
|
||||
* Slow shift to daytime target Temperature
|
||||
**/
|
||||
void resetSlowUpdateTimer();
|
||||
|
||||
void updateSunTimings(bool force);
|
||||
DateTimes getSunTimings(QDate date, double latitude, double longitude, bool morning) const;
|
||||
bool checkAutomaticSunTimings() const;
|
||||
bool daylight() const;
|
||||
|
||||
void commitGammaRamps(int temperature);
|
||||
|
||||
ColorCorrectDBusInterface *m_iface;
|
||||
|
||||
bool m_active;
|
||||
bool m_running = false;
|
||||
|
||||
NightColorMode m_mode = NightColorMode::Automatic;
|
||||
|
||||
// the previous and next sunrise/sunset intervals - in UTC time
|
||||
DateTimes m_prev = DateTimes();
|
||||
DateTimes m_next = DateTimes();
|
||||
|
||||
// manual times from config
|
||||
QTime m_morning = QTime(6,0);
|
||||
QTime m_evening = QTime(18,0);
|
||||
int m_trTime = 30; // saved in minutes > 1
|
||||
|
||||
// auto location provided by work space
|
||||
double m_latAuto;
|
||||
double m_lngAuto;
|
||||
// manual location from config
|
||||
double m_latFixed;
|
||||
double m_lngFixed;
|
||||
|
||||
QTimer *m_slowUpdateStartTimer = nullptr;
|
||||
QTimer *m_slowUpdateTimer = nullptr;
|
||||
QTimer *m_quickAdjustTimer = nullptr;
|
||||
|
||||
int m_currentTemp = NEUTRAL_TEMPERATURE;
|
||||
int m_dayTargetTemp = NEUTRAL_TEMPERATURE;
|
||||
int m_nightTargetTemp = DEFAULT_NIGHT_TEMPERATURE;
|
||||
|
||||
int m_failedCommitAttempts = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KWIN_COLORCORRECT_MANAGER_H
|
|
@ -0,0 +1,163 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#include "suncalc.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <qmath.h>
|
||||
|
||||
namespace KWin {
|
||||
namespace ColorCorrect {
|
||||
|
||||
#define TWILIGHT_NAUT -12.0
|
||||
#define TWILIGHT_CIVIL -6.0
|
||||
#define SUN_RISE_SET -0.833
|
||||
#define SUN_HIGH 2.0
|
||||
|
||||
QPair<QTime, QTime> calculateSunTimings(QDate prompt, double latitude, double longitude, bool morning)
|
||||
{
|
||||
// calculations based on http://aa.quae.nl/en/reken/zonpositie.html
|
||||
// accuracy: +/- 5min
|
||||
|
||||
// positioning
|
||||
const double rad = M_PI / 180.;
|
||||
const double earthObliquity = 23.4397; // epsilon
|
||||
|
||||
const double lat = latitude; // phi
|
||||
const double lng = -longitude; // lw
|
||||
|
||||
// times
|
||||
const double juPrompt = prompt.toJulianDay(); // J
|
||||
const double ju2000 = 2451545.; // J2000
|
||||
|
||||
// geometry
|
||||
auto mod360 = [](double number) -> double {
|
||||
return std::fmod(number, 360.);
|
||||
};
|
||||
|
||||
auto sin = [&rad](double angle) -> double {
|
||||
return std::sin(angle * rad);
|
||||
};
|
||||
auto cos = [&rad](double angle) -> double {
|
||||
return std::cos(angle * rad);
|
||||
};
|
||||
auto asin = [&rad](double val) -> double {
|
||||
return std::asin(val) / rad;
|
||||
};
|
||||
auto acos = [&rad](double val) -> double {
|
||||
return std::acos(val) / rad;
|
||||
};
|
||||
|
||||
auto anomaly = [&](const double date) -> double { // M
|
||||
return mod360(357.5291 + 0.98560028 * (date - ju2000));
|
||||
};
|
||||
|
||||
auto center = [&sin](double anomaly) -> double { // C
|
||||
return 1.9148 * sin(anomaly) + 0.02 * sin(2 * anomaly) + 0.0003 * sin(3 * anomaly);
|
||||
};
|
||||
|
||||
auto ecliptLngMean = [](double anom) -> double { // Mean ecliptical longitude L_sun = Mean Anomaly + Perihelion + 180°
|
||||
return anom + 282.9372; // anom + 102.9372 + 180°
|
||||
};
|
||||
|
||||
auto ecliptLng = [&](double anom) -> double { // lambda = L_sun + C
|
||||
return ecliptLngMean(anom) + center(anom);
|
||||
};
|
||||
|
||||
auto declination = [&](const double date) -> double { // delta
|
||||
const double anom = anomaly(date);
|
||||
const double eclLng = ecliptLng(anom);
|
||||
|
||||
return mod360(asin(sin(earthObliquity) * sin(eclLng)));
|
||||
};
|
||||
|
||||
// sun hour angle at specific angle
|
||||
auto hourAngle = [&](const double date, double angle) -> double { // H_t
|
||||
const double decl = declination(date);
|
||||
const double ret0 = (sin(angle) - sin(lat) * sin(decl)) / (cos(lat) * cos(decl));
|
||||
|
||||
double ret = mod360(acos( ret0 ));
|
||||
if (180. < ret) {
|
||||
ret = ret - 360.;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/*
|
||||
* Sun positions
|
||||
*/
|
||||
|
||||
// transit is at noon
|
||||
auto getTransit = [&](const double date) -> double { // Jtransit
|
||||
const double juMeanSolTime = juPrompt - ju2000 - 0.0009 - lng / 360.; // n_x = J - J_2000 - J_0 - l_w / 360°
|
||||
const double juTrEstimate = date + qRound64(juMeanSolTime) - juMeanSolTime; // J_x = J + n - n_x
|
||||
const double anom = anomaly(juTrEstimate); // M
|
||||
const double eclLngM = ecliptLngMean(anom); // L_sun
|
||||
|
||||
return juTrEstimate + 0.0053 * sin(anom) - 0.0068 * sin(2 * eclLngM);
|
||||
};
|
||||
|
||||
auto getSunMorning = [&hourAngle](const double angle, const double transit) -> double {
|
||||
return transit - hourAngle(transit, angle) / 360.;
|
||||
};
|
||||
|
||||
auto getSunEvening = [&hourAngle](const double angle, const double transit) -> double {
|
||||
return transit + hourAngle(transit, angle) / 360.;
|
||||
};
|
||||
|
||||
/*
|
||||
* Begin calculations
|
||||
*/
|
||||
|
||||
// noon - sun at the highest point
|
||||
const double juNoon = getTransit(juPrompt);
|
||||
|
||||
double begin, end;
|
||||
if (morning) {
|
||||
begin = getSunMorning(TWILIGHT_CIVIL, juNoon);
|
||||
end = getSunMorning(SUN_HIGH, juNoon);
|
||||
} else {
|
||||
begin = getSunEvening(SUN_HIGH, juNoon);
|
||||
end = getSunEvening(TWILIGHT_CIVIL, juNoon);
|
||||
}
|
||||
// transform to QDateTime
|
||||
begin += 0.5;
|
||||
end += 0.5;
|
||||
|
||||
QTime timeBegin, timeEnd;
|
||||
|
||||
if (std::isnan(begin)) {
|
||||
timeBegin = QTime();
|
||||
} else {
|
||||
double timePart = begin - (int)begin;
|
||||
timeBegin = QTime::fromMSecsSinceStartOfDay((int)( timePart * MSC_DAY ));
|
||||
}
|
||||
if (std::isnan(end)) {
|
||||
timeEnd = QTime();
|
||||
} else {
|
||||
double timePart = end - (int)end;
|
||||
timeEnd = QTime::fromMSecsSinceStartOfDay((int)( timePart * MSC_DAY ));
|
||||
}
|
||||
|
||||
return QPair<QTime,QTime> (timeBegin, timeEnd);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2017 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_SUNCALCULATOR_H
|
||||
#define KWIN_SUNCALCULATOR_H
|
||||
|
||||
#include <QDate>
|
||||
#include <QTime>
|
||||
#include <QPair>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
namespace ColorCorrect
|
||||
{
|
||||
|
||||
/**
|
||||
* Calculates for a given location and date two of the
|
||||
* following sun timings in their temporal order:
|
||||
* - Nautical dawn and sunrise for the morning
|
||||
* - Sunset and nautical dusk for the evening
|
||||
* @since 5.12
|
||||
**/
|
||||
|
||||
QPair<QTime, QTime> calculateSunTimings(QDate prompt, double latitude, double longitude, bool morning);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KWIN_SUNCALCULATOR_H
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.kwin.ColorCorrect">
|
||||
<method name="nightColorInfo">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QHash<QString,QVariant>"/>
|
||||
<arg type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="setNightColorConfig">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QHash<QString,QVariant>"/>
|
||||
<arg type="a{sv}" direction="in"/>
|
||||
<arg type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="nightColorAutoLocationUpdate">
|
||||
<arg type="d" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
</method>
|
||||
<signal name="nightColorConfigChanged">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QHash<QString,QVariant>"/>
|
||||
<arg type="a{sv}" direction="out"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
|
@ -30,6 +30,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "scene.h"
|
||||
#include "screenedge.h"
|
||||
#include "wayland_server.h"
|
||||
#include "colorcorrection/manager.h"
|
||||
|
||||
#include <KWayland/Server/outputconfiguration_interface.h>
|
||||
|
||||
namespace KWin
|
||||
|
@ -39,6 +41,7 @@ Platform::Platform(QObject *parent)
|
|||
: QObject(parent)
|
||||
, m_eglDisplay(EGL_NO_DISPLAY)
|
||||
{
|
||||
m_colorCorrect = new ColorCorrect::Manager(this);
|
||||
}
|
||||
|
||||
Platform::~Platform()
|
||||
|
|
31
platform.h
31
platform.h
|
@ -38,6 +38,10 @@ namespace KWayland {
|
|||
|
||||
namespace KWin
|
||||
{
|
||||
namespace ColorCorrect {
|
||||
class Manager;
|
||||
struct GammaRamp;
|
||||
}
|
||||
|
||||
class Edge;
|
||||
class Compositor;
|
||||
|
@ -383,6 +387,28 @@ public:
|
|||
**/
|
||||
virtual QVector<CompositingType> supportedCompositors() const = 0;
|
||||
|
||||
/**
|
||||
* Whether gamma control is supported by the backend.
|
||||
* @since 5.12
|
||||
**/
|
||||
bool supportsGammaControl() const {
|
||||
return m_supportsGammaControl;
|
||||
}
|
||||
|
||||
ColorCorrect::Manager *colorCorrectManager() {
|
||||
return m_colorCorrect;
|
||||
}
|
||||
|
||||
virtual int gammaRampSize(int screen) const {
|
||||
Q_UNUSED(screen);
|
||||
return 0;
|
||||
}
|
||||
virtual bool setGammaRamp(int screen, ColorCorrect::GammaRamp &gamma) {
|
||||
Q_UNUSED(screen);
|
||||
Q_UNUSED(gamma);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* A string of information to include in kwin debug output
|
||||
* It should not be translated.
|
||||
|
@ -444,6 +470,9 @@ protected:
|
|||
void setSupportsPointerWarping(bool set) {
|
||||
m_pointerWarping = set;
|
||||
}
|
||||
void setSupportsGammaControl(bool set) {
|
||||
m_supportsGammaControl = set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual platform specific way to hide the cursor.
|
||||
|
@ -488,6 +517,8 @@ private:
|
|||
EGLContext m_context = EGL_NO_CONTEXT;
|
||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
||||
int m_hideCursorCounter = 0;
|
||||
ColorCorrect::Manager *m_colorCorrect = nullptr;
|
||||
bool m_supportsGammaControl = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "screens_drm.h"
|
||||
#include "udev.h"
|
||||
#include "wayland_server.h"
|
||||
#include <colorcorrection/gammaramp.h>
|
||||
#if HAVE_GBM
|
||||
#include "egl_gbm_backend.h"
|
||||
#include <gbm.h>
|
||||
|
@ -73,6 +74,7 @@ DrmBackend::DrmBackend(QObject *parent)
|
|||
, m_udevMonitor(m_udev->monitor())
|
||||
, m_dpmsFilter()
|
||||
{
|
||||
setSupportsGammaControl(true);
|
||||
handleOutputs();
|
||||
}
|
||||
|
||||
|
@ -759,6 +761,22 @@ QVector<CompositingType> DrmBackend::supportedCompositors() const
|
|||
#endif
|
||||
}
|
||||
|
||||
int DrmBackend::gammaRampSize(int screen) const
|
||||
{
|
||||
if (m_outputs.size() <= screen) {
|
||||
return 0;
|
||||
}
|
||||
return m_outputs.at(screen)->m_crtc->getGammaRampSize();
|
||||
}
|
||||
|
||||
bool DrmBackend::setGammaRamp(int screen, ColorCorrect::GammaRamp &gamma)
|
||||
{
|
||||
if (m_outputs.size() <= screen) {
|
||||
return false;
|
||||
}
|
||||
return m_outputs.at(screen)->m_crtc->setGammaRamp(gamma);
|
||||
}
|
||||
|
||||
QString DrmBackend::supportInformation() const
|
||||
{
|
||||
QString supportInfo;
|
||||
|
|
|
@ -55,6 +55,10 @@ class OutputManagementInterface;
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
namespace ColorCorrect {
|
||||
struct GammaRamp;
|
||||
}
|
||||
|
||||
class Udev;
|
||||
class UdevMonitor;
|
||||
|
||||
|
@ -120,6 +124,8 @@ public:
|
|||
gbm_device *gbmDevice() const {
|
||||
return m_gbmDevice;
|
||||
}
|
||||
int gammaRampSize(int screen) const override;
|
||||
bool setGammaRamp(int screen, ColorCorrect::GammaRamp &gamma) override;
|
||||
|
||||
QVector<CompositingType> supportedCompositors() const override;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "drm_output.h"
|
||||
#include "drm_buffer.h"
|
||||
#include "logging.h"
|
||||
#include <colorcorrection/gammaramp.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -31,6 +32,10 @@ DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex)
|
|||
m_resIndex(resIndex),
|
||||
m_backend(backend)
|
||||
{
|
||||
ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(backend->fd(), crtc_id));
|
||||
if (modeCrtc) {
|
||||
m_gammaRampSize = modeCrtc->gamma_size;
|
||||
}
|
||||
}
|
||||
|
||||
DrmCrtc::~DrmCrtc()
|
||||
|
@ -107,4 +112,10 @@ bool DrmCrtc::blank()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool DrmCrtc::setGammaRamp(ColorCorrect::GammaRamp &gamma) {
|
||||
bool isError = drmModeCrtcSetGamma(m_backend->fd(), m_id, gamma.size,
|
||||
gamma.red, gamma.green, gamma.blue);
|
||||
return !isError;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
namespace ColorCorrect {
|
||||
struct GammaRamp;
|
||||
}
|
||||
|
||||
class DrmBackend;
|
||||
class DrmBuffer;
|
||||
class DrmDumbBuffer;
|
||||
|
@ -63,8 +67,14 @@ public:
|
|||
void flipBuffer();
|
||||
bool blank();
|
||||
|
||||
int getGammaRampSize() const {
|
||||
return m_gammaRampSize;
|
||||
}
|
||||
bool setGammaRamp(ColorCorrect::GammaRamp &gamma);
|
||||
|
||||
private:
|
||||
int m_resIndex;
|
||||
uint32_t m_gammaRampSize = 0;
|
||||
|
||||
DrmBuffer *m_currentBuffer = nullptr;
|
||||
DrmBuffer *m_nextBuffer = nullptr;
|
||||
|
|
|
@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#if HAVE_GBM
|
||||
#include <gbm.h>
|
||||
#endif
|
||||
#include <colorcorrection/manager.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -50,6 +51,7 @@ VirtualBackend::VirtualBackend(QObject *parent)
|
|||
}
|
||||
}
|
||||
setSupportsPointerWarping(true);
|
||||
setSupportsGammaControl(true);
|
||||
}
|
||||
|
||||
VirtualBackend::~VirtualBackend()
|
||||
|
@ -98,4 +100,13 @@ OpenGLBackend *VirtualBackend::createOpenGLBackend()
|
|||
return new EglGbmBackend(this);
|
||||
}
|
||||
|
||||
int VirtualBackend::gammaRampSize(int screen) const {
|
||||
return m_gammaSizes[screen];
|
||||
}
|
||||
|
||||
bool VirtualBackend::setGammaRamp(int screen, ColorCorrect::GammaRamp &gamma) {
|
||||
Q_UNUSED(gamma);
|
||||
return m_gammaResults[screen];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ struct gbm_device;
|
|||
|
||||
namespace KWin
|
||||
{
|
||||
namespace ColorCorrect {
|
||||
class Manager;
|
||||
struct GammaRamp;
|
||||
}
|
||||
|
||||
|
||||
class KWIN_EXPORT VirtualBackend : public Platform
|
||||
{
|
||||
|
@ -65,6 +70,8 @@ public:
|
|||
|
||||
Q_INVOKABLE void setOutputCount(int count) {
|
||||
m_outputCount = count;
|
||||
m_gammaSizes = QVector<int>(count, 200);
|
||||
m_gammaResults = QVector<bool>(count, true);
|
||||
}
|
||||
|
||||
Q_INVOKABLE void setOutputScale(qreal scale) {
|
||||
|
@ -84,6 +91,8 @@ public:
|
|||
void setGbmDevice(gbm_device *device) {
|
||||
m_gbmDevice = device;
|
||||
}
|
||||
virtual int gammaRampSize(int screen) const override;
|
||||
virtual bool setGammaRamp(int screen, ColorCorrect::GammaRamp &gamma) override;
|
||||
|
||||
QVector<CompositingType> supportedCompositors() const override {
|
||||
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
|
||||
|
@ -100,6 +109,9 @@ private:
|
|||
QScopedPointer<QTemporaryDir> m_screenshotDir;
|
||||
int m_drmFd = -1;
|
||||
gbm_device *m_gbmDevice = nullptr;
|
||||
|
||||
QVector<int> m_gammaSizes = QVector<int>(1, 200);
|
||||
QVector<bool> m_gammaResults = QVector<bool>(1, true);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue