[gnome-settings-daemon] color: Add natural light functionality
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] color: Add natural light functionality
- Date: Thu, 9 Feb 2017 16:54:45 +0000 (UTC)
commit 749c9cf675906483ebd29beb5a4ac379a7613d3f
Author: Richard Hughes <richard hughsie com>
Date: Tue Jan 31 16:54:09 2017 +0000
color: Add natural light functionality
https://bugzilla.gnome.org/show_bug.cgi?id=778039
configure.ac | 1 +
...settings-daemon.plugins.color.gschema.xml.in.in | 30 +
plugins/color/Makefile.am | 6 +
plugins/color/gcm-self-test.c | 114 ++++
plugins/color/gsd-color-manager.c | 128 ++++-
plugins/color/gsd-natural-light.c | 577 ++++++++++++++++++++
plugins/color/gsd-natural-light.h | 50 ++
7 files changed, 899 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6e0d0dc..b42b644 100644
--- a/configure.ac
+++ b/configure.ac
@@ -161,6 +161,7 @@ PKG_CHECK_MODULES(COLOR,
colord >= 1.0.2
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION
libcanberra-gtk3
+ libgeoclue-2.0 >= $GEOCLUE_REQUIRED_VERSION
lcms2 >= $LCMS_REQUIRED_VERSION
libnotify)
diff --git a/data/org.gnome.settings-daemon.plugins.color.gschema.xml.in.in
b/data/org.gnome.settings-daemon.plugins.color.gschema.xml.in.in
index 3df07c7..7fb36a0 100644
--- a/data/org.gnome.settings-daemon.plugins.color.gschema.xml.in.in
+++ b/data/org.gnome.settings-daemon.plugins.color.gschema.xml.in.in
@@ -10,5 +10,35 @@
<_summary>The duration a printer profile is valid</_summary>
<_description>This is the number of days after which the printer color profile is considered
invalid.</_description>
</key>
+ <key name="natural-light-enabled" type="b">
+ <default>false</default>
+ <_summary>If the natural night mode is enabled</_summary>
+ <_description>Natural light mode changes the color temperature of your display when the sun has gone
down or at present times.</_description>
+ </key>
+ <key name="natural-light-temperature" type="u">
+ <default>4000</default>
+ <_summary>Temperature of the display when enabled</_summary>
+ <_description>This temperature in Kelvin is used to modify the screen tones when natural light mode is
enabled. Higher values are bluer, lower redder.</_description>
+ </key>
+ <key name="natural-light-schedule-automatic" type="b">
+ <default>true</default>
+ <_summary>Use the sunrise and sunset</_summary>
+ <_description>Calculate the sunrise and sunset times automatically, from the current
location.</_description>
+ </key>
+ <key name="natural-light-schedule-from" type="d">
+ <default>16.00</default>
+ <_summary>The start time</_summary>
+ <_description>When “natural-light-schedule-automatic” is disabled, use this start time in hours from
midnight.</_description>
+ </key>
+ <key name="natural-light-schedule-to" type="d">
+ <default>8.00</default>
+ <_summary>The end time</_summary>
+ <_description>When “natural-light-schedule-automatic” is disabled, use this end time in hours from
midnight.</_description>
+ </key>
+ <key name="natural-light-last-coordinates" type="(dd)">
+ <default>(181,181)</default>
+ <_summary>The last detected position</_summary>
+ <_description>When location services are available this represents the last detected location. The
default value is an invalid value to ensure it is always updated at startup.</_description>
+ </key>
</schema>
</schemalist>
diff --git a/plugins/color/Makefile.am b/plugins/color/Makefile.am
index a8e9567..042b245 100644
--- a/plugins/color/Makefile.am
+++ b/plugins/color/Makefile.am
@@ -15,8 +15,12 @@ gcm_self_test_CFLAGS = \
gcm_self_test_SOURCES = \
gcm-edid.c \
gcm-edid.h \
+ gsd-natural-light.c \
+ gsd-natural-light.h \
gsd-natural-light-common.c \
gsd-natural-light-common.h \
+ gnome-datetime-source.c \
+ gnome-datetime-source.h \
gcm-self-test.c
gcm_self_test_LDADD = \
@@ -41,6 +45,8 @@ gsd_color_SOURCES = \
gsd-color-profiles.h \
gsd-color-state.c \
gsd-color-state.h \
+ gsd-natural-light.c \
+ gsd-natural-light.h \
gsd-natural-light-common.c \
gsd-natural-light-common.h \
$(NULL)
diff --git a/plugins/color/gcm-self-test.c b/plugins/color/gcm-self-test.c
index a5163a7..efb2101 100644
--- a/plugins/color/gcm-self-test.c
+++ b/plugins/color/gcm-self-test.c
@@ -26,9 +26,120 @@
#include <gtk/gtk.h>
#include "gcm-edid.h"
+#include "gsd-color-state.h"
+#include "gsd-natural-light.h"
#include "gsd-natural-light-common.h"
static void
+on_notify (GsdNaturalLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ guint *cnt = (guint *) user_data;
+ (*cnt)++;
+}
+
+static void
+gcm_test_natural_light (void)
+{
+ gboolean ret;
+ guint disabled_until_tmw_cnt = 0;
+ guint sunrise_cnt = 0;
+ guint sunset_cnt = 0;
+ guint temperature_cnt = 0;
+ g_autoptr(GDateTime) datetime_override = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsdNaturalLight) nlight = NULL;
+ g_autoptr(GSettings) settings = NULL;
+
+ nlight = gsd_natural_light_new ();
+ g_assert (GSD_IS_NATURAL_LIGHT (nlight));
+ g_signal_connect (nlight, "notify::sunset",
+ G_CALLBACK (on_notify), &sunset_cnt);
+ g_signal_connect (nlight, "notify::sunrise",
+ G_CALLBACK (on_notify), &sunrise_cnt);
+ g_signal_connect (nlight, "notify::temperature",
+ G_CALLBACK (on_notify), &temperature_cnt);
+ g_signal_connect (nlight, "notify::disabled-until-tmw",
+ G_CALLBACK (on_notify), &disabled_until_tmw_cnt);
+
+ /* hardcode a specific date and time */
+ datetime_override = g_date_time_new_utc (2017, 2, 8, 20, 0, 0);
+ gsd_natural_light_set_date_time_now (nlight, datetime_override);
+
+ /* do not start geoclue */
+ gsd_natural_light_set_geoclue_enabled (nlight, FALSE);
+
+ /* switch off */
+ settings = g_settings_new ("org.gnome.settings-daemon.plugins.color");
+ g_settings_set_boolean (settings, "natural-light-enabled", FALSE);
+
+ /* check default values */
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunrise (nlight), ==, -1);
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunset (nlight), ==, -1);
+ g_assert_cmpint (gsd_natural_light_get_temperature (nlight), ==, GSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (!gsd_natural_light_get_disabled_until_tmw (nlight));
+
+ /* start module, disabled */
+ ret = gsd_natural_light_start (nlight, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpint (sunset_cnt, ==, 0);
+ g_assert_cmpint (sunrise_cnt, ==, 0);
+ g_assert_cmpint (temperature_cnt, ==, 0);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 0);
+
+ /* enable automatic mode */
+ g_settings_set_value (settings, "natural-light-last-coordinates",
+ g_variant_new ("(dd)", 51.5, -0.1278));
+ g_settings_set_boolean (settings, "natural-light-schedule-automatic", TRUE);
+ g_settings_set_boolean (settings, "natural-light-enabled", TRUE);
+ g_assert_cmpint (sunset_cnt, ==, 1);
+ g_assert_cmpint (sunrise_cnt, ==, 1);
+ g_assert_cmpint (temperature_cnt, ==, 1);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 0);
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunrise (nlight), ==, 7);
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunset (nlight), ==, 17);
+ g_assert_cmpint (gsd_natural_light_get_temperature (nlight), ==, 4000);
+ g_assert (!gsd_natural_light_get_disabled_until_tmw (nlight));
+
+ /* disable for one day */
+ gsd_natural_light_set_disabled_until_tmw (nlight, TRUE);
+ gsd_natural_light_set_disabled_until_tmw (nlight, TRUE);
+ g_assert_cmpint (gsd_natural_light_get_temperature (nlight), ==, GSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (gsd_natural_light_get_disabled_until_tmw (nlight));
+ g_assert_cmpint (temperature_cnt, ==, 2);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 1);
+
+ /* change our mind */
+ gsd_natural_light_set_disabled_until_tmw (nlight, FALSE);
+ g_assert_cmpint (gsd_natural_light_get_temperature (nlight), ==, 4000);
+ g_assert (!gsd_natural_light_get_disabled_until_tmw (nlight));
+ g_assert_cmpint (temperature_cnt, ==, 3);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 2);
+
+ /* enabled manual mode (night shift) */
+ g_settings_set_double (settings, "natural-light-schedule-from", 4.0);
+ g_settings_set_double (settings, "natural-light-schedule-to", 16.f);
+ g_settings_set_boolean (settings, "natural-light-schedule-automatic", FALSE);
+ g_assert_cmpint (sunset_cnt, ==, 1);
+ g_assert_cmpint (sunrise_cnt, ==, 1);
+ g_assert_cmpint (temperature_cnt, ==, 4);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 2);
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunrise (nlight), ==, 7);
+ g_assert_cmpint ((gint) gsd_natural_light_get_sunset (nlight), ==, 17);
+ g_assert_cmpint (gsd_natural_light_get_temperature (nlight), ==, GSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (!gsd_natural_light_get_disabled_until_tmw (nlight));
+
+ /* finally disable, with no changes */
+ g_settings_set_boolean (settings, "natural-light-enabled", FALSE);
+ g_assert_cmpint (sunset_cnt, ==, 1);
+ g_assert_cmpint (sunrise_cnt, ==, 1);
+ g_assert_cmpint (temperature_cnt, ==, 4);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 2);
+}
+
+static void
gcm_test_edid_func (void)
{
GcmEdid *edid;
@@ -131,12 +242,15 @@ gcm_test_frac_day (void)
int
main (int argc, char **argv)
{
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+
gtk_init (&argc, &argv);
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/color/edid", gcm_test_edid_func);
g_test_add_func ("/color/sunset-sunrise", gcm_test_sunset_sunrise);
g_test_add_func ("/color/fractional-day", gcm_test_frac_day);
+ g_test_add_func ("/color/natural-light", gcm_test_natural_light);
return g_test_run ();
}
diff --git a/plugins/color/gsd-color-manager.c b/plugins/color/gsd-color-manager.c
index 6a1c745..0834af5 100644
--- a/plugins/color/gsd-color-manager.c
+++ b/plugins/color/gsd-color-manager.c
@@ -29,6 +29,7 @@
#include "gsd-color-manager.h"
#include "gsd-color-profiles.h"
#include "gsd-color-state.h"
+#include "gsd-natural-light.h"
#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
@@ -42,6 +43,9 @@ static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gnome.SettingsDaemon.Color'>"
" <property name='Temperature' type='u' access='readwrite'/>"
+" <property name='DisabledUntilTomorrow' type='b' access='readwrite'/>"
+" <property name='Sunrise' type='d' access='read'/>"
+" <property name='Sunset' type='d' access='read'/>"
" </interface>"
"</node>";
@@ -58,6 +62,7 @@ struct GsdColorManagerPrivate
GsdColorCalibrate *calibrate;
GsdColorProfiles *profiles;
GsdColorState *state;
+ GsdNaturalLight *nlight;
};
enum {
@@ -123,6 +128,86 @@ gsd_color_manager_class_init (GsdColorManagerClass *klass)
}
static void
+emit_property_changed (GsdColorManager *manager,
+ const gchar *property_name,
+ GVariant *property_value)
+{
+ GsdColorManagerPrivate *priv = manager->priv;
+ GVariantBuilder builder;
+ GVariantBuilder invalidated_builder;
+
+ /* not yet connected */
+ if (priv->connection == NULL)
+ return;
+
+ /* build the dict */
+ g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&builder,
+ "{sv}",
+ property_name,
+ property_value);
+ g_dbus_connection_emit_signal (priv->connection,
+ NULL,
+ GSD_COLOR_DBUS_PATH,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ GSD_COLOR_DBUS_INTERFACE,
+ &builder,
+ &invalidated_builder),
+ NULL);
+ g_variant_builder_clear (&builder);
+ g_variant_builder_clear (&invalidated_builder);
+}
+
+static void
+on_sunset_notify (GsdNaturalLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
+ GsdColorManagerPrivate *priv = manager->priv;
+ emit_property_changed (manager, "Sunset",
+ g_variant_new_double (gsd_natural_light_get_sunset (priv->nlight)));
+}
+
+static void
+on_sunrise_notify (GsdNaturalLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
+ GsdColorManagerPrivate *priv = manager->priv;
+ emit_property_changed (manager, "Sunrise",
+ g_variant_new_double (gsd_natural_light_get_sunrise (priv->nlight)));
+}
+
+static void
+on_disabled_until_tmw_notify (GsdNaturalLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
+ GsdColorManagerPrivate *priv = manager->priv;
+ emit_property_changed (manager, "DisabledUntilTomorrow",
+ g_variant_new_boolean (gsd_natural_light_get_disabled_until_tmw
(priv->nlight)));
+}
+
+static void
+on_tempertature_notify (GsdNaturalLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
+ GsdColorManagerPrivate *priv = manager->priv;
+ gdouble temperature = gsd_natural_light_get_temperature (priv->nlight);
+ gsd_color_state_set_temperature (priv->state, temperature);
+ emit_property_changed (manager, "Temperature",
+ g_variant_new_double (temperature));
+}
+
+static void
gsd_color_manager_init (GsdColorManager *manager)
{
GsdColorManagerPrivate *priv;
@@ -132,6 +217,17 @@ gsd_color_manager_init (GsdColorManager *manager)
priv->calibrate = gsd_color_calibrate_new ();
priv->profiles = gsd_color_profiles_new ();
priv->state = gsd_color_state_new ();
+
+ /* natural light features */
+ priv->nlight = gsd_natural_light_new ();
+ g_signal_connect (priv->nlight, "notify::sunset",
+ G_CALLBACK (on_sunset_notify), manager);
+ g_signal_connect (priv->nlight, "notify::sunrise",
+ G_CALLBACK (on_sunrise_notify), manager);
+ g_signal_connect (priv->nlight, "notify::temperature",
+ G_CALLBACK (on_tempertature_notify), manager);
+ g_signal_connect (priv->nlight, "notify::disabled-until-tmw",
+ G_CALLBACK (on_disabled_until_tmw_notify), manager);
}
static void
@@ -162,6 +258,7 @@ gsd_color_manager_finalize (GObject *object)
g_clear_object (&manager->priv->calibrate);
g_clear_object (&manager->priv->profiles);
g_clear_object (&manager->priv->state);
+ g_clear_object (&manager->priv->nlight);
G_OBJECT_CLASS (gsd_color_manager_parent_class)->finalize (object);
}
@@ -174,7 +271,6 @@ handle_get_property (GDBusConnection *connection,
const gchar *property_name,
GError **error, gpointer user_data)
{
- GVariant *retval = NULL;
GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
GsdColorManagerPrivate *priv = manager->priv;
@@ -187,15 +283,21 @@ handle_get_property (GDBusConnection *connection,
if (g_strcmp0 (property_name, "Temperature") == 0) {
guint temperature;
temperature = gsd_color_state_get_temperature (priv->state);
- retval = g_variant_new_uint32 (temperature);
+ return g_variant_new_uint32 (temperature);
}
- if (retval == NULL) {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "Failed to get property: %s", property_name);
- }
+ if (g_strcmp0 (property_name, "DisabledUntilTomorrow") == 0)
+ return g_variant_new_boolean (gsd_natural_light_get_disabled_until_tmw (priv->nlight));
+
+ if (g_strcmp0 (property_name, "Sunrise") == 0)
+ return g_variant_new_double (gsd_natural_light_get_sunrise (priv->nlight));
- return retval;
+ if (g_strcmp0 (property_name, "Sunset") == 0)
+ return g_variant_new_double (gsd_natural_light_get_sunset (priv->nlight));
+
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "Failed to get property: %s", property_name);
+ return NULL;
}
static gboolean
@@ -239,6 +341,12 @@ handle_set_property (GDBusConnection *connection,
return TRUE;
}
+ if (g_strcmp0 (property_name, "DisabledUntilTomorrow") == 0) {
+ gsd_natural_light_set_disabled_until_tmw (priv->nlight,
+ g_variant_get_boolean (value));
+ return TRUE;
+ }
+
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
"No such property: %s", property_name);
return FALSE;
@@ -292,6 +400,12 @@ on_bus_gotten (GObject *source_object,
name_lost_handler_cb,
manager,
NULL);
+
+ /* setup natural light module */
+ if (!gsd_natural_light_start (priv->nlight, &error)) {
+ g_warning ("Could not start natural light module: %s", error->message);
+ g_error_free (error);
+ }
}
static void
diff --git a/plugins/color/gsd-natural-light.c b/plugins/color/gsd-natural-light.c
new file mode 100644
index 0000000..dedbd74
--- /dev/null
+++ b/plugins/color/gsd-natural-light.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <geoclue.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-datetime-source.h"
+
+#include "gsd-color-state.h"
+
+#include "gsd-natural-light.h"
+#include "gsd-natural-light-common.h"
+
+struct _GsdNaturalLight {
+ GObject parent;
+ GSettings *settings;
+ gboolean disabled_until_tmw;
+ gboolean geoclue_enabled;
+ GSource *natural_light_source;
+ guint natural_light_validate_id;
+ gint disabled_day_of_month;
+ GClueClient *geoclue_client;
+ GClueSimple *geoclue_simple;
+ gdouble cached_sunrise;
+ gdouble cached_sunset;
+ gdouble cached_temperature;
+ GCancellable *cancellable;
+ GDateTime *datetime_override;
+};
+
+enum {
+ PROP_0,
+ PROP_SUNRISE,
+ PROP_SUNSET,
+ PROP_TEMPERATURE,
+ PROP_DISABLED_UNTIL_TMW,
+ PROP_LAST
+};
+
+#define GSD_NATURAL_LIGHT_SCHEDULE_TIMEOUT 5 /* seconds */
+#define GSD_NATURAL_LIGHT_POLL_TIMEOUT 60 /* seconds */
+#define GSD_NATURAL_LIGHT_POLL_SMEAR 1 /* hours */
+
+#define GSD_FRAC_DAY_MAX_DELTA (1.f/60.f) /* 1 minute */
+#define GSD_TEMPERATURE_MAX_DELTA (10.f) /* Kelvin */
+
+#define DESKTOP_ID "gnome-color-panel"
+
+static void poll_timeout_destroy (GsdNaturalLight *self);
+static void poll_timeout_create (GsdNaturalLight *self);
+
+G_DEFINE_TYPE (GsdNaturalLight, gsd_natural_light, G_TYPE_OBJECT);
+
+static GDateTime *
+gsd_natural_light_get_date_time_now (GsdNaturalLight *self)
+{
+ if (self->datetime_override != NULL)
+ return g_date_time_ref (self->datetime_override);
+ return g_date_time_new_now_local ();
+}
+
+void
+gsd_natural_light_set_date_time_now (GsdNaturalLight *self, GDateTime *datetime)
+{
+ if (self->datetime_override != NULL)
+ g_date_time_unref (self->datetime_override);
+ self->datetime_override = g_date_time_ref (datetime);
+}
+
+static gdouble
+linear_interpolate (gdouble val1, gdouble val2, gdouble factor)
+{
+ g_return_val_if_fail (factor > 0.f, -1.f);
+ g_return_val_if_fail (factor < 1.f, -1.f);
+ return ((val1 - val2) * factor) + val2;
+}
+
+static gboolean
+update_cached_sunrise_sunset (GsdNaturalLight *self)
+{
+ gboolean ret = FALSE;
+ gdouble latitude;
+ gdouble longitude;
+ gdouble sunrise;
+ gdouble sunset;
+ g_autoptr(GVariant) tmp = NULL;
+ g_autoptr(GDateTime) dt_now = gsd_natural_light_get_date_time_now (self);
+
+ /* calculate the sunrise/sunset for the location */
+ tmp = g_settings_get_value (self->settings, "natural-light-last-coordinates");
+ g_variant_get (tmp, "(dd)", &latitude, &longitude);
+ if (latitude > 180.f || latitude < -180.f)
+ return FALSE;
+ if (longitude > 180.f || longitude < -180.f)
+ return FALSE;
+ if (!gsd_natural_light_get_sunrise_sunset (dt_now, latitude, longitude,
+ &sunrise, &sunset)) {
+ g_warning ("failed to get sunset/sunrise for %.3f,%.3f",
+ longitude, longitude);
+ return FALSE;
+ }
+
+ /* anything changed */
+ if (ABS (self->cached_sunrise - sunrise) > GSD_FRAC_DAY_MAX_DELTA) {
+ self->cached_sunrise = sunrise;
+ g_object_notify (G_OBJECT (self), "sunrise");
+ ret = TRUE;
+ }
+ if (ABS (self->cached_sunset - sunset) > GSD_FRAC_DAY_MAX_DELTA) {
+ self->cached_sunset = sunset;
+ g_object_notify (G_OBJECT (self), "sunset");
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static void
+gsd_natural_light_set_temperature (GsdNaturalLight *self, gdouble temperature)
+{
+ if (ABS (self->cached_temperature - temperature) > GSD_TEMPERATURE_MAX_DELTA) {
+ self->cached_temperature = temperature;
+ g_object_notify (G_OBJECT (self), "temperature");
+ }
+}
+
+static void
+natural_light_recheck (GsdNaturalLight *self)
+{
+ gdouble frac_day;
+ gdouble schedule_from = -1.f;
+ gdouble schedule_to = -1.f;
+ gdouble smear = GSD_NATURAL_LIGHT_POLL_SMEAR; /* hours */
+ guint temperature;
+ guint temp_smeared;
+ g_autoptr(GDateTime) dt_now = gsd_natural_light_get_date_time_now (self);
+
+ /* enabled */
+ if (!g_settings_get_boolean (self->settings, "natural-light-enabled")) {
+ g_debug ("natural light disabled, resetting");
+ gsd_natural_light_set_temperature (self,
+ GSD_COLOR_TEMPERATURE_DEFAULT);
+ return;
+ }
+
+ /* disabled until tomorrow */
+ if (self->disabled_until_tmw) {
+ gint tmp_day_of_month = g_date_time_get_day_of_month (dt_now);
+ if (tmp_day_of_month == self->disabled_day_of_month) {
+ g_debug ("natural light still day-disabled, resetting");
+ gsd_natural_light_set_temperature (self,
+ GSD_COLOR_TEMPERATURE_DEFAULT);
+ return;
+ }
+
+ /* no longer valid */
+ self->disabled_day_of_month = 0;
+ self->disabled_until_tmw = FALSE;
+ g_object_notify (G_OBJECT (self), "disabled-until-tmw");
+ }
+
+ /* calculate the position of the sun */
+ if (g_settings_get_boolean (self->settings, "natural-light-schedule-automatic")) {
+ update_cached_sunrise_sunset (self);
+ if (self->cached_sunrise > 0.f && self->cached_sunset > 0.f) {
+ schedule_to = self->cached_sunrise;
+ schedule_from = self->cached_sunset;
+ }
+ }
+
+ /* fall back to manual settings */
+ if (schedule_to <= 0.f || schedule_from <= 0.f) {
+ schedule_from = g_settings_get_double (self->settings,
+ "natural-light-schedule-from");
+ schedule_to = g_settings_get_double (self->settings,
+ "natural-light-schedule-to");
+ }
+
+ /* get the current hour of a day as a fraction */
+ frac_day = gsd_natural_light_frac_day_from_dt (dt_now);
+ g_debug ("fractional day = %.3f, limits = %.3f->%.3f",
+ frac_day, schedule_from, schedule_to);
+ if (!gsd_natural_light_frac_day_is_between (frac_day,
+ schedule_from - smear,
+ schedule_to)) {
+ g_debug ("not time for natural-light");
+ gsd_natural_light_set_temperature (self,
+ GSD_COLOR_TEMPERATURE_DEFAULT);
+ return;
+ }
+
+ /* smear the temperature for a short duration before the set limits
+ *
+ * |----------------------| = from->to
+ * |-| = smear down
+ * |-| = smear up
+ *
+ * \ /
+ * \ /
+ * \--------------------/
+ */
+ temperature = g_settings_get_uint (self->settings, "natural-light-temperature");
+ if (gsd_natural_light_frac_day_is_between (frac_day,
+ schedule_from - smear,
+ schedule_from)) {
+ gdouble factor = 1.f - ((frac_day - (schedule_from - smear)) / smear);
+ temp_smeared = linear_interpolate (GSD_COLOR_TEMPERATURE_DEFAULT,
+ temperature, factor);
+ } else if (gsd_natural_light_frac_day_is_between (frac_day,
+ schedule_to - smear,
+ schedule_to)) {
+ gdouble factor = ((schedule_to - smear) - frac_day) / smear;
+ temp_smeared = linear_interpolate (GSD_COLOR_TEMPERATURE_DEFAULT,
+ temperature, factor);
+ } else {
+ temp_smeared = temperature;
+ }
+ g_debug ("natural light mode on, using temperature of %uK (aiming for %uK)",
+ temp_smeared, temperature);
+ gsd_natural_light_set_temperature (self, temp_smeared);
+}
+
+static gboolean
+natural_light_recheck_schedule_cb (gpointer user_data)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (user_data);
+ natural_light_recheck (self);
+ self->natural_light_validate_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+/* called when something changed */
+static void
+natural_light_recheck_schedule (GsdNaturalLight *self)
+{
+ if (self->natural_light_validate_id != 0)
+ g_source_remove (self->natural_light_validate_id);
+ self->natural_light_validate_id =
+ g_timeout_add_seconds (GSD_NATURAL_LIGHT_SCHEDULE_TIMEOUT,
+ natural_light_recheck_schedule_cb,
+ self);
+}
+
+/* called when the time may have changed */
+static gboolean
+natural_light_recheck_cb (gpointer user_data)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (user_data);
+
+ /* recheck parameters, then reschedule a new timeout */
+ natural_light_recheck (self);
+ poll_timeout_destroy (self);
+ poll_timeout_create (self);
+
+ /* return value ignored for a one-time watch */
+ return G_SOURCE_REMOVE;
+}
+
+static void
+poll_timeout_create (GsdNaturalLight *self)
+{
+ g_autoptr(GDateTime) dt_now = NULL;
+ g_autoptr(GDateTime) dt_expiry = NULL;
+
+ if (self->natural_light_source != NULL)
+ return;
+
+ dt_now = gsd_natural_light_get_date_time_now (self);
+ dt_expiry = g_date_time_add_seconds (dt_now, GSD_NATURAL_LIGHT_POLL_TIMEOUT);
+ self->natural_light_source = _gnome_datetime_source_new (dt_now,
+ dt_expiry,
+ FALSE);
+ g_source_set_callback (self->natural_light_source,
+ natural_light_recheck_cb,
+ self, NULL);
+ g_source_attach (self->natural_light_source, NULL);
+}
+
+static void
+poll_timeout_destroy (GsdNaturalLight *self)
+{
+
+ if (self->natural_light_source == NULL)
+ return;
+
+ g_source_destroy (self->natural_light_source);
+ self->natural_light_source = NULL;
+}
+
+static void
+settings_changed_cb (GSettings *settings, gchar *key, gpointer user_data)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (user_data);
+ g_debug ("settings changed");
+ natural_light_recheck (self);
+}
+
+static void
+on_location_notify (GClueSimple *simple,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (user_data);
+ GClueLocation *location;
+ gdouble latitude, longitude;
+
+ location = gclue_simple_get_location (simple);
+ latitude = gclue_location_get_latitude (location);
+ longitude = gclue_location_get_longitude (location);
+
+ g_settings_set_value (self->settings,
+ "natural-light-last-coordinates",
+ g_variant_new ("(dd)", latitude, longitude));
+
+ g_debug ("got geoclue latitude %f, longitude %f", latitude, longitude);
+
+ /* recheck the levels if the location changed significantly */
+ if (update_cached_sunrise_sunset (self))
+ natural_light_recheck_schedule (self);
+}
+
+static void
+on_geoclue_simple_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (user_data);
+ GClueSimple *geoclue_simple;
+ g_autoptr(GError) error = NULL;
+
+ geoclue_simple = gclue_simple_new_finish (res, &error);
+ if (geoclue_simple == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to connect to GeoClue2 service: %s", error->message);
+ return;
+ }
+
+ self->geoclue_simple = geoclue_simple;
+ self->geoclue_client = gclue_simple_get_client (self->geoclue_simple);
+
+ g_signal_connect (self->geoclue_simple, "notify::location",
+ G_CALLBACK (on_location_notify), user_data);
+
+ on_location_notify (self->geoclue_simple, NULL, user_data);
+}
+
+static void
+register_geoclue (GsdNaturalLight *self)
+{
+ gclue_simple_new (DESKTOP_ID,
+ GCLUE_ACCURACY_LEVEL_CITY,
+ self->cancellable,
+ on_geoclue_simple_ready,
+ self);
+
+}
+
+void
+gsd_natural_light_set_disabled_until_tmw (GsdNaturalLight *self, gboolean value)
+{
+ g_autoptr(GDateTime) dt = gsd_natural_light_get_date_time_now (self);
+
+ if (self->disabled_until_tmw == value)
+ return;
+
+ self->disabled_until_tmw = value;
+ self->disabled_day_of_month = g_date_time_get_day_of_month (dt);
+ natural_light_recheck (self);
+ g_object_notify (G_OBJECT (self), "disabled-until-tmw");
+}
+
+gboolean
+gsd_natural_light_get_disabled_until_tmw (GsdNaturalLight *self)
+{
+ return self->disabled_until_tmw;
+}
+
+gdouble
+gsd_natural_light_get_sunrise (GsdNaturalLight *self)
+{
+ return self->cached_sunrise;
+}
+
+gdouble
+gsd_natural_light_get_sunset (GsdNaturalLight *self)
+{
+ return self->cached_sunset;
+}
+
+gdouble
+gsd_natural_light_get_temperature (GsdNaturalLight *self)
+{
+ return self->cached_temperature;
+}
+
+void
+gsd_natural_light_set_geoclue_enabled (GsdNaturalLight *self, gboolean enabled)
+{
+ self->geoclue_enabled = enabled;
+}
+
+gboolean
+gsd_natural_light_start (GsdNaturalLight *self, GError **error)
+{
+
+ if (self->geoclue_enabled)
+ register_geoclue (self);
+
+ natural_light_recheck (self);
+ poll_timeout_create (self);
+
+ /* care about changes */
+ g_signal_connect (self->settings, "changed",
+ G_CALLBACK (settings_changed_cb), self);
+ return TRUE;
+}
+
+static void
+gsd_natural_light_finalize (GObject *object)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (object);
+
+ poll_timeout_destroy (self);
+
+ g_clear_object (&self->settings);
+ g_clear_pointer (&self->datetime_override, (GDestroyNotify) g_date_time_unref);
+
+ if (self->cancellable != NULL) {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ }
+
+ if (self->natural_light_validate_id > 0) {
+ g_source_remove (self->natural_light_validate_id);
+ self->natural_light_validate_id = 0;
+ }
+
+ if (self->geoclue_client != NULL)
+ gclue_client_call_stop (self->geoclue_client, NULL, NULL, NULL);
+ g_clear_object (&self->geoclue_simple);
+ G_OBJECT_CLASS (gsd_natural_light_parent_class)->finalize (object);
+}
+
+static void
+gsd_natural_light_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (object);
+
+ switch (prop_id) {
+ case PROP_SUNRISE:
+ self->cached_sunrise = g_value_get_double (value);
+ break;
+ case PROP_SUNSET:
+ self->cached_sunset = g_value_get_double (value);
+ break;
+ case PROP_TEMPERATURE:
+ self->cached_temperature = g_value_get_double (value);
+ break;
+ case PROP_DISABLED_UNTIL_TMW:
+ self->disabled_until_tmw = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gsd_natural_light_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsdNaturalLight *self = GSD_NATURAL_LIGHT (object);
+
+ switch (prop_id) {
+ case PROP_SUNRISE:
+ g_value_set_double (value, self->cached_sunrise);
+ break;
+ case PROP_SUNSET:
+ g_value_set_double (value, self->cached_sunrise);
+ break;
+ case PROP_TEMPERATURE:
+ g_value_set_double (value, self->cached_sunrise);
+ break;
+ case PROP_DISABLED_UNTIL_TMW:
+ g_value_set_boolean (value, gsd_natural_light_get_disabled_until_tmw (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gsd_natural_light_class_init (GsdNaturalLightClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gsd_natural_light_finalize;
+
+ object_class->set_property = gsd_natural_light_set_property;
+ object_class->get_property = gsd_natural_light_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_SUNRISE,
+ g_param_spec_double ("sunrise",
+ "Sunrise",
+ "Sunrise in fractional hours",
+ 0,
+ 24.f,
+ 12,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SUNSET,
+ g_param_spec_double ("sunset",
+ "Sunset",
+ "Sunset in fractional hours",
+ 0,
+ 24.f,
+ 12,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_TEMPERATURE,
+ g_param_spec_double ("temperature",
+ "Temperature",
+ "Temperature in Kelvin",
+ GSD_COLOR_TEMPERATURE_MIN,
+ GSD_COLOR_TEMPERATURE_MAX,
+ GSD_COLOR_TEMPERATURE_DEFAULT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_DISABLED_UNTIL_TMW,
+ g_param_spec_boolean ("disabled-until-tmw",
+ "Disabled until tomorrow",
+ "If the natural light is disabled until the
next day",
+ FALSE,
+ G_PARAM_READWRITE));
+
+}
+
+static void
+gsd_natural_light_init (GsdNaturalLight *self)
+{
+ self->cached_sunrise = -1.f;
+ self->cached_sunset = -1.f;
+ self->cached_temperature = GSD_COLOR_TEMPERATURE_DEFAULT;
+ self->cancellable = g_cancellable_new ();
+ self->settings = g_settings_new ("org.gnome.settings-daemon.plugins.color");
+}
+
+GsdNaturalLight *
+gsd_natural_light_new (void)
+{
+ return g_object_new (GSD_TYPE_NATURAL_LIGHT, NULL);
+}
diff --git a/plugins/color/gsd-natural-light.h b/plugins/color/gsd-natural-light.h
new file mode 100644
index 0000000..8f5a295
--- /dev/null
+++ b/plugins/color/gsd-natural-light.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_NATURAL_LIGHT_H__
+#define __GSD_NATURAL_LIGHT_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_NATURAL_LIGHT (gsd_natural_light_get_type ())
+G_DECLARE_FINAL_TYPE (GsdNaturalLight, gsd_natural_light, GSD, NATURAL_LIGHT, GObject)
+
+GsdNaturalLight *gsd_natural_light_new (void);
+gboolean gsd_natural_light_start (GsdNaturalLight *self,
+ GError **error);
+gdouble gsd_natural_light_get_sunrise (GsdNaturalLight *self);
+gdouble gsd_natural_light_get_sunset (GsdNaturalLight *self);
+gdouble gsd_natural_light_get_temperature (GsdNaturalLight *self);
+
+gboolean gsd_natural_light_get_disabled_until_tmw (GsdNaturalLight *self);
+void gsd_natural_light_set_disabled_until_tmw (GsdNaturalLight *self,
+ gboolean value);
+
+/* only for the self test program */
+void gsd_natural_light_set_geoclue_enabled (GsdNaturalLight *self,
+ gboolean enabled);
+void gsd_natural_light_set_date_time_now (GsdNaturalLight *self,
+ GDateTime *datetime);
+
+G_END_DECLS
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]