1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #pragma once
11 
12 #include "constants.h"
13 #include "plugin.h"
14 
15 #include <QObject>
16 #include <QPair>
17 #include <QDateTime>
18 
19 class QTimer;
20 
21 namespace KWin
22 {
23 
24 class ClockSkewNotifier;
25 class NightColorDBusInterface;
26 
27 typedef QPair<QDateTime,QDateTime> DateTimes;
28 typedef QPair<QTime,QTime> Times;
29 
30 /**
31  * This enum type is used to specify operation mode of the night color manager.
32  */
33 enum NightColorMode {
34     /**
35      * Color temperature is computed based on the current position of the Sun.
36      *
37      * Location of the user is provided by Plasma.
38      */
39     Automatic,
40     /**
41      * Color temperature is computed based on the current position of the Sun.
42      *
43      * Location of the user is provided by themselves.
44      */
45     Location,
46     /**
47      * Color temperature is computed based on the current time.
48      *
49      * Sunrise and sunset times have to be specified by the user.
50      */
51     Timings,
52     /**
53      * Color temperature is constant thoughout the day.
54      */
55     Constant,
56 };
57 
58 /**
59  * The night color manager is a blue light filter similar to Redshift.
60  *
61  * There are four modes this manager can operate in: Automatic, Location, Timings,
62  * and Constant. Both Automatic and Location modes derive screen color temperature
63  * from the current position of the Sun, the only difference between two is how
64  * coordinates of the user are specified. If the user is located near the North or
65  * South pole, we can't compute correct position of the Sun, that's why we need
66  * Timings and Constant mode.
67  *
68  * With the Timings mode, screen color temperature is computed based on the clock
69  * time. The user needs to specify timings of the sunset and sunrise as well the
70  * transition time.
71  *
72  * With the Constant mode, screen color temperature is always constant.
73  */
74 class KWIN_EXPORT NightColorManager : public Plugin
75 {
76     Q_OBJECT
77 
78 public:
79     explicit NightColorManager(QObject *parent = nullptr);
80     ~NightColorManager() override;
81 
82     void init();
83 
84     /**
85      * Get current configuration
86      * @see changeConfiguration
87      * @since 5.12
88      */
89     QHash<QString, QVariant> info() const;
90     /**
91      * Change configuration
92      * @see info
93      * @since 5.12
94      */
95     bool changeConfiguration(QHash<QString, QVariant> data);
96     void autoLocationUpdate(double latitude, double longitude);
97 
98     /**
99      * Toggles the active state of the filter.
100      *
101      * A quick transition will be started if the difference between current screen
102      * color temperature and target screen color temperature is too large. Target
103      * temperature is defined in context of the new active state.
104      *
105      * If the filter becomes inactive after calling this method, the target color
106      * temperature is 6500 K.
107      *
108      * If the filter becomes active after calling this method, the target screen
109      * color temperature is defined by the current operation mode.
110      *
111      * Note that this method is a no-op if the underlying platform doesn't support
112      * adjusting gamma ramps.
113      */
114     void toggle();
115 
116     /**
117      * Returns @c true if the night color manager is blocked; otherwise @c false.
118      */
119     bool isInhibited() const;
120 
121     /**
122      * Temporarily blocks the night color manager.
123      *
124      * After calling this method, the screen color temperature will be reverted
125      * back to 6500C. When you're done, call uninhibit() method.
126      */
127     void inhibit();
128 
129     /**
130      * Attempts to unblock the night color manager.
131      */
132     void uninhibit();
133 
134     /**
135      * Returns @c true if Night Color is enabled; otherwise @c false.
136      */
137     bool isEnabled() const;
138 
139     /**
140      * Returns @c true if Night Color is currently running; otherwise @c false.
141      */
142     bool isRunning() const;
143 
144     /**
145      * Returns @c true if Night Color is supported by platform; otherwise @c false.
146      */
147     bool isAvailable() const;
148 
149     /**
150      * Returns the current screen color temperature.
151      */
152     int currentTemperature() const;
153 
154     /**
155      * Returns the target screen color temperature.
156      */
157     int targetTemperature() const;
158 
159     /**
160      * Returns the mode in which Night Color is operating.
161      */
162     NightColorMode mode() const;
163 
164     /**
165      * Returns the datetime that specifies when the previous screen color temperature transition
166      * had started. Notice that when Night Color operates in the Constant mode, the returned date
167      * time object is not valid.
168      */
169     QDateTime previousTransitionDateTime() const;
170 
171     /**
172      * Returns the duration of the previous screen color temperature transition, in milliseconds.
173      */
174     qint64 previousTransitionDuration() const;
175 
176     /**
177      * Returns the datetime that specifies when the next screen color temperature transition will
178      * start. Notice that when Night Color operates in the Constant mode, the returned date time
179      * object is not valid.
180      */
181     QDateTime scheduledTransitionDateTime() const;
182 
183     /**
184      * Returns the duration of the next screen color temperature transition, in milliseconds.
185      */
186     qint64 scheduledTransitionDuration() const;
187 
188     // for auto tests
189     void reparseConfigAndReset();
190     static NightColorManager *self();
191 
192 public Q_SLOTS:
193     void resetSlowUpdateStartTimer();
194     void quickAdjust();
195 
196 Q_SIGNALS:
197     void configChange(QHash<QString, QVariant> data);
198 
199     /**
200      * Emitted whenever the night color manager is blocked or unblocked.
201      */
202     void inhibitedChanged();
203 
204     /**
205      * Emitted whenever the night color manager is enabled or disabled.
206      */
207     void enabledChanged();
208 
209     /**
210      * Emitted whenever the night color manager starts or stops running.
211      */
212     void runningChanged();
213 
214     /**
215      * Emitted whenever the current screen color temperature has changed.
216      */
217     void currentTemperatureChanged();
218 
219     /**
220      * Emitted whenever the target screen color temperature has changed.
221      */
222     void targetTemperatureChanged();
223 
224     /**
225      * Emitted whenver the operation mode has changed.
226      */
227     void modeChanged();
228 
229     /**
230      * Emitted whenever the timings of the previous color temperature transition have changed.
231      */
232     void previousTransitionTimingsChanged();
233 
234     /**
235      * Emitted whenever the timings of the next color temperature transition have changed.
236      */
237     void scheduledTransitionTimingsChanged();
238 
239 private:
240     void readConfig();
241     void hardReset();
242     void slowUpdate(int targetTemp);
243     void resetAllTimers();
244     int currentTargetTemp() const;
245     void cancelAllTimers();
246     /**
247      * Quick shift on manual change to current target Temperature
248      */
249     void resetQuickAdjustTimer();
250     /**
251      * Slow shift to daytime target Temperature
252      */
253     void resetSlowUpdateTimer();
254 
255     void updateTargetTemperature();
256     void updateTransitionTimings(bool force);
257     DateTimes getSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning) const;
258     bool checkAutomaticSunTimings() const;
259     bool daylight() const;
260 
261     void commitGammaRamps(int temperature);
262 
263     void setEnabled(bool enabled);
264     void setRunning(bool running);
265     void setCurrentTemperature(int temperature);
266     void setMode(NightColorMode mode);
267 
268     NightColorDBusInterface *m_iface;
269     ClockSkewNotifier *m_skewNotifier;
270 
271     // Specifies whether Night Color is enabled.
272     bool m_active = false;
273 
274     // Specifies whether Night Color is currently running.
275     bool m_running = false;
276 
277     // Specifies whether Night Color is inhibited globally.
278     bool m_isGloballyInhibited = false;
279 
280     NightColorMode m_mode = NightColorMode::Automatic;
281 
282     // the previous and next sunrise/sunset intervals - in UTC time
283     DateTimes m_prev = DateTimes();
284     DateTimes m_next = DateTimes();
285 
286     // manual times from config
287     QTime m_morning = QTime(6,0);
288     QTime m_evening = QTime(18,0);
289     int m_trTime = 30; // saved in minutes > 1
290 
291     // auto location provided by work space
292     double m_latAuto;
293     double m_lngAuto;
294     // manual location from config
295     double m_latFixed;
296     double m_lngFixed;
297 
298     QTimer *m_slowUpdateStartTimer = nullptr;
299     QTimer *m_slowUpdateTimer = nullptr;
300     QTimer *m_quickAdjustTimer = nullptr;
301 
302     int m_currentTemp = NEUTRAL_TEMPERATURE;
303     int m_targetTemperature = NEUTRAL_TEMPERATURE;
304     int m_dayTargetTemp = NEUTRAL_TEMPERATURE;
305     int m_nightTargetTemp = DEFAULT_NIGHT_TEMPERATURE;
306 
307     int m_inhibitReferenceCount = 0;
308 };
309 
310 } // namespace KWin
311