diff --git a/data/org.cinnamon.settings-daemon.plugins.color.gschema.xml.in.in b/data/org.cinnamon.settings-daemon.plugins.color.gschema.xml.in.in
index e37376c0..986e2e3c 100644
--- a/data/org.cinnamon.settings-daemon.plugins.color.gschema.xml.in.in
+++ b/data/org.cinnamon.settings-daemon.plugins.color.gschema.xml.in.in
@@ -10,5 +10,35 @@
The duration a printer profile is valid
This is the number of days after which the printer color profile is considered invalid.
+
+ false
+ If the night light mode is enabled
+ Night light mode changes the color temperature of your display when the sun has gone down or at preset times.
+
+
+ 2700
+ Temperature of the display when enabled
+ This temperature in Kelvin is used to modify the screen tones when night light mode is enabled. Higher values are bluer, lower redder.
+
+
+ true
+ Use the sunrise and sunset
+ Calculate the sunrise and sunset times automatically, from the current location.
+
+
+ 20.00
+ The start time
+ When “night-light-schedule-automatic” is disabled, use this start time in hours from midnight.
+
+
+ 6.00
+ The end time
+ When “night-light-schedule-automatic” is disabled, use this end time in hours from midnight.
+
+
+ (91,181)
+ The last detected position
+ 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.
+
diff --git a/meson.build b/meson.build
index 7c92bd84..3410fa8a 100644
--- a/meson.build
+++ b/meson.build
@@ -98,6 +98,8 @@ endif
cc = meson.get_compiler('c')
math = cc.find_library('m', required: false)
+has_timerfd_create = cc.has_function('timerfd_create')
+
csd_conf = configuration_data()
csd_conf.set_quoted('GTKBUILDERDIR', gtkbuilderdir)
csd_conf.set_quoted('CINNAMON_SETTINGS_LOCALEDIR', localedir)
@@ -108,6 +110,7 @@ csd_conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
csd_conf.set_quoted('LIBEXECDIR', join_paths(prefix, libexecdir))
csd_conf.set_quoted('SYSCONFDIR', sysconfdir)
csd_conf.set_quoted('LIBDIR', libdir)
+csd_conf.set10('HAVE_TIMERFD', has_timerfd_create)
if gudev.found()
cargs += '-DHAVE_GUDEV'
diff --git a/plugins/a11y-settings/meson.build b/plugins/a11y-settings/meson.build
index 2759250e..cdf88f53 100644
--- a/plugins/a11y-settings/meson.build
+++ b/plugins/a11y-settings/meson.build
@@ -17,6 +17,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: a11y_settings_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/automount/meson.build b/plugins/automount/meson.build
index bba6e4ca..93e2d951 100644
--- a/plugins/automount/meson.build
+++ b/plugins/automount/meson.build
@@ -18,6 +18,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: automount_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install_rpath: join_paths(prefix, apilibdir),
diff --git a/plugins/background/meson.build b/plugins/background/meson.build
index 2056d1a0..f722207c 100644
--- a/plugins/background/meson.build
+++ b/plugins/background/meson.build
@@ -19,6 +19,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: background_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/clipboard/meson.build b/plugins/clipboard/meson.build
index 016adca7..6d033c47 100644
--- a/plugins/clipboard/meson.build
+++ b/plugins/clipboard/meson.build
@@ -19,6 +19,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: clipboard_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/color/ccm-edid.c b/plugins/color/ccm-edid.c
new file mode 100644
index 00000000..29142b09
--- /dev/null
+++ b/plugins/color/ccm-edid.c
@@ -0,0 +1,450 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Soren Sandmann
+ * Copyright (C) 2009-2011 Richard Hughes
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#include "ccm-edid.h"
+
+static void ccm_edid_finalize (GObject *object);
+
+struct _CcmEdid
+{
+ GObject parent;
+
+ gchar *monitor_name;
+ gchar *vendor_name;
+ gchar *serial_number;
+ gchar *eisa_id;
+ gchar *checksum;
+ gchar *pnp_id;
+ guint width;
+ guint height;
+ gfloat gamma;
+ CdColorYxy *red;
+ CdColorYxy *green;
+ CdColorYxy *blue;
+ CdColorYxy *white;
+ GnomePnpIds *pnp_ids;
+};
+
+G_DEFINE_TYPE (CcmEdid, ccm_edid, G_TYPE_OBJECT)
+
+#define CCM_EDID_OFFSET_PNPID 0x08
+#define CCM_EDID_OFFSET_SERIAL 0x0c
+#define CCM_EDID_OFFSET_SIZE 0x15
+#define CCM_EDID_OFFSET_GAMMA 0x17
+#define CCM_EDID_OFFSET_DATA_BLOCKS 0x36
+#define CCM_EDID_OFFSET_LAST_BLOCK 0x6c
+#define CCM_EDID_OFFSET_EXTENSION_BLOCK_COUNT 0x7e
+
+#define CCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
+#define CCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
+#define CCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA 0xf9
+#define CCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
+#define CCM_DESCRIPTOR_COLOR_POINT 0xfb
+
+GQuark
+ccm_edid_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("ccm_edid_error");
+ return quark;
+}
+
+const gchar *
+ccm_edid_get_monitor_name (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->monitor_name;
+}
+
+const gchar *
+ccm_edid_get_vendor_name (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+
+ if (edid->vendor_name == NULL)
+ edid->vendor_name = gnome_pnp_ids_get_pnp_id (edid->pnp_ids, edid->pnp_id);
+ return edid->vendor_name;
+}
+
+const gchar *
+ccm_edid_get_serial_number (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->serial_number;
+}
+
+const gchar *
+ccm_edid_get_eisa_id (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->eisa_id;
+}
+
+const gchar *
+ccm_edid_get_checksum (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->checksum;
+}
+
+const gchar *
+ccm_edid_get_pnp_id (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->pnp_id;
+}
+
+guint
+ccm_edid_get_width (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), 0);
+ return edid->width;
+}
+
+guint
+ccm_edid_get_height (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), 0);
+ return edid->height;
+}
+
+gfloat
+ccm_edid_get_gamma (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), 0.0f);
+ return edid->gamma;
+}
+
+const CdColorYxy *
+ccm_edid_get_red (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->red;
+}
+
+const CdColorYxy *
+ccm_edid_get_green (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->green;
+}
+
+const CdColorYxy *
+ccm_edid_get_blue (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->blue;
+}
+
+const CdColorYxy *
+ccm_edid_get_white (CcmEdid *edid)
+{
+ g_return_val_if_fail (CCM_IS_EDID (edid), NULL);
+ return edid->white;
+}
+
+void
+ccm_edid_reset (CcmEdid *edid)
+{
+ g_return_if_fail (CCM_IS_EDID (edid));
+
+ /* free old data */
+ g_free (edid->monitor_name);
+ g_free (edid->vendor_name);
+ g_free (edid->serial_number);
+ g_free (edid->eisa_id);
+ g_free (edid->checksum);
+
+ /* do not deallocate, just blank */
+ edid->pnp_id[0] = '\0';
+
+ /* set to default values */
+ edid->monitor_name = NULL;
+ edid->vendor_name = NULL;
+ edid->serial_number = NULL;
+ edid->eisa_id = NULL;
+ edid->checksum = NULL;
+ edid->width = 0;
+ edid->height = 0;
+ edid->gamma = 0.0f;
+}
+
+static gint
+ccm_edid_get_bit (gint in, gint bit)
+{
+ return (in & (1 << bit)) >> bit;
+}
+
+/**
+ * ccm_edid_get_bits:
+ **/
+static gint
+ccm_edid_get_bits (gint in, gint begin, gint end)
+{
+ gint mask = (1 << (end - begin + 1)) - 1;
+
+ return (in >> begin) & mask;
+}
+
+/**
+ * ccm_edid_decode_fraction:
+ **/
+static gdouble
+ccm_edid_decode_fraction (gint high, gint low)
+{
+ gdouble result = 0.0;
+ gint i;
+
+ high = (high << 2) | low;
+ for (i = 0; i < 10; ++i)
+ result += ccm_edid_get_bit (high, i) * pow (2, i - 10);
+ return result;
+}
+
+static gchar *
+ccm_edid_parse_string (const guint8 *data)
+{
+ gchar *text;
+ guint i;
+ guint replaced = 0;
+
+ /* this is always 13 bytes, but we can't guarantee it's null
+ * terminated or not junk. */
+ text = g_strndup ((const gchar *) data, 13);
+
+ /* remove insane newline chars */
+ g_strdelimit (text, "\n\r", '\0');
+
+ /* remove spaces */
+ g_strchomp (text);
+
+ /* nothing left? */
+ if (text[0] == '\0') {
+ g_free (text);
+ text = NULL;
+ goto out;
+ }
+
+ /* ensure string is printable */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (!g_ascii_isprint (text[i])) {
+ text[i] = '-';
+ replaced++;
+ }
+ }
+
+ /* if the string is junk, ignore the string */
+ if (replaced > 4) {
+ g_free (text);
+ text = NULL;
+ goto out;
+ }
+out:
+ return text;
+}
+
+gboolean
+ccm_edid_parse (CcmEdid *edid, const guint8 *data, gsize length, GError **error)
+{
+ gboolean ret = TRUE;
+ guint i;
+ guint32 serial;
+ gchar *tmp;
+
+ /* check header */
+ if (length < 128) {
+ g_set_error_literal (error,
+ CCM_EDID_ERROR,
+ CCM_EDID_ERROR_FAILED_TO_PARSE,
+ "EDID length is too small");
+ ret = FALSE;
+ goto out;
+ }
+ if (data[0] != 0x00 || data[1] != 0xff) {
+ g_set_error_literal (error,
+ CCM_EDID_ERROR,
+ CCM_EDID_ERROR_FAILED_TO_PARSE,
+ "Failed to parse EDID header");
+ ret = FALSE;
+ goto out;
+ }
+
+ /* free old data */
+ ccm_edid_reset (edid);
+
+ /* decode the PNP ID from three 5 bit words packed into 2 bytes
+ * /--08--\/--09--\
+ * 7654321076543210
+ * |\---/\---/\---/
+ * R C1 C2 C3 */
+ edid->pnp_id[0] = 'A' + ((data[CCM_EDID_OFFSET_PNPID+0] & 0x7c) / 4) - 1;
+ edid->pnp_id[1] = 'A' + ((data[CCM_EDID_OFFSET_PNPID+0] & 0x3) * 8) + ((data[CCM_EDID_OFFSET_PNPID+1] & 0xe0) / 32) - 1;
+ edid->pnp_id[2] = 'A' + (data[CCM_EDID_OFFSET_PNPID+1] & 0x1f) - 1;
+
+ /* maybe there isn't a ASCII serial number descriptor, so use this instead */
+ serial = (guint32) data[CCM_EDID_OFFSET_SERIAL+0];
+ serial += (guint32) data[CCM_EDID_OFFSET_SERIAL+1] * 0x100;
+ serial += (guint32) data[CCM_EDID_OFFSET_SERIAL+2] * 0x10000;
+ serial += (guint32) data[CCM_EDID_OFFSET_SERIAL+3] * 0x1000000;
+ if (serial > 0)
+ edid->serial_number = g_strdup_printf ("%" G_GUINT32_FORMAT, serial);
+
+ /* get the size */
+ edid->width = data[CCM_EDID_OFFSET_SIZE+0];
+ edid->height = data[CCM_EDID_OFFSET_SIZE+1];
+
+ /* we don't care about aspect */
+ if (edid->width == 0 || edid->height == 0) {
+ edid->width = 0;
+ edid->height = 0;
+ }
+
+ /* get gamma */
+ if (data[CCM_EDID_OFFSET_GAMMA] == 0xff) {
+ edid->gamma = 1.0f;
+ } else {
+ edid->gamma = ((gfloat) data[CCM_EDID_OFFSET_GAMMA] / 100) + 1;
+ }
+
+ /* get color red */
+ edid->red->x = ccm_edid_decode_fraction (data[0x1b], ccm_edid_get_bits (data[0x19], 6, 7));
+ edid->red->y = ccm_edid_decode_fraction (data[0x1c], ccm_edid_get_bits (data[0x19], 4, 5));
+
+ /* get color green */
+ edid->green->x = ccm_edid_decode_fraction (data[0x1d], ccm_edid_get_bits (data[0x19], 2, 3));
+ edid->green->y = ccm_edid_decode_fraction (data[0x1e], ccm_edid_get_bits (data[0x19], 0, 1));
+
+ /* get color blue */
+ edid->blue->x = ccm_edid_decode_fraction (data[0x1f], ccm_edid_get_bits (data[0x1a], 6, 7));
+ edid->blue->y = ccm_edid_decode_fraction (data[0x20], ccm_edid_get_bits (data[0x1a], 4, 5));
+
+ /* get color white */
+ edid->white->x = ccm_edid_decode_fraction (data[0x21], ccm_edid_get_bits (data[0x1a], 2, 3));
+ edid->white->y = ccm_edid_decode_fraction (data[0x22], ccm_edid_get_bits (data[0x1a], 0, 1));
+
+ /* parse EDID data */
+ for (i = CCM_EDID_OFFSET_DATA_BLOCKS;
+ i <= CCM_EDID_OFFSET_LAST_BLOCK;
+ i += 18) {
+ /* ignore pixel clock data */
+ if (data[i] != 0)
+ continue;
+ if (data[i+2] != 0)
+ continue;
+
+ /* any useful blocks? */
+ if (data[i+3] == CCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
+ tmp = ccm_edid_parse_string (&data[i+5]);
+ if (tmp != NULL) {
+ g_free (edid->monitor_name);
+ edid->monitor_name = tmp;
+ }
+ } else if (data[i+3] == CCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
+ tmp = ccm_edid_parse_string (&data[i+5]);
+ if (tmp != NULL) {
+ g_free (edid->serial_number);
+ edid->serial_number = tmp;
+ }
+ } else if (data[i+3] == CCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA) {
+ g_warning ("failing to parse color management data");
+ } else if (data[i+3] == CCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
+ tmp = ccm_edid_parse_string (&data[i+5]);
+ if (tmp != NULL) {
+ g_free (edid->eisa_id);
+ edid->eisa_id = tmp;
+ }
+ } else if (data[i+3] == CCM_DESCRIPTOR_COLOR_POINT) {
+ if (data[i+3+9] != 0xff) {
+ /* extended EDID block(1) which contains
+ * a better gamma value */
+ edid->gamma = ((gfloat) data[i+3+9] / 100) + 1;
+ }
+ if (data[i+3+14] != 0xff) {
+ /* extended EDID block(2) which contains
+ * a better gamma value */
+ edid->gamma = ((gfloat) data[i+3+9] / 100) + 1;
+ }
+ }
+ }
+
+ /* calculate checksum */
+ edid->checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, length);
+out:
+ return ret;
+}
+
+static void
+ccm_edid_class_init (CcmEdidClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = ccm_edid_finalize;
+}
+
+static void
+ccm_edid_init (CcmEdid *edid)
+{
+ edid->pnp_ids = gnome_pnp_ids_new ();
+ edid->pnp_id = g_new0 (gchar, 4);
+ edid->red = cd_color_yxy_new ();
+ edid->green = cd_color_yxy_new ();
+ edid->blue = cd_color_yxy_new ();
+ edid->white = cd_color_yxy_new ();
+}
+
+static void
+ccm_edid_finalize (GObject *object)
+{
+ CcmEdid *edid = CCM_EDID (object);
+
+ g_free (edid->monitor_name);
+ g_free (edid->vendor_name);
+ g_free (edid->serial_number);
+ g_free (edid->eisa_id);
+ g_free (edid->checksum);
+ g_free (edid->pnp_id);
+ cd_color_yxy_free (edid->white);
+ cd_color_yxy_free (edid->red);
+ cd_color_yxy_free (edid->green);
+ cd_color_yxy_free (edid->blue);
+ g_object_unref (edid->pnp_ids);
+
+ G_OBJECT_CLASS (ccm_edid_parent_class)->finalize (object);
+}
+
+CcmEdid *
+ccm_edid_new (void)
+{
+ CcmEdid *edid;
+ edid = g_object_new (CCM_TYPE_EDID, NULL);
+ return CCM_EDID (edid);
+}
+
diff --git a/plugins/color/ccm-edid.h b/plugins/color/ccm-edid.h
new file mode 100644
index 00000000..38e4fccd
--- /dev/null
+++ b/plugins/color/ccm-edid.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2010 Richard Hughes
+ *
+ * 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 __CCM_EDID_H
+#define __CCM_EDID_H
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define CCM_TYPE_EDID (ccm_edid_get_type ())
+G_DECLARE_FINAL_TYPE (CcmEdid, ccm_edid, CCM, EDID, GObject)
+
+#define CCM_EDID_ERROR (ccm_edid_error_quark ())
+enum
+{
+ CCM_EDID_ERROR_FAILED_TO_PARSE
+};
+
+GQuark ccm_edid_error_quark (void);
+CcmEdid *ccm_edid_new (void);
+void ccm_edid_reset (CcmEdid *edid);
+gboolean ccm_edid_parse (CcmEdid *edid,
+ const guint8 *data,
+ gsize length,
+ GError **error);
+const gchar *ccm_edid_get_monitor_name (CcmEdid *edid);
+const gchar *ccm_edid_get_vendor_name (CcmEdid *edid);
+const gchar *ccm_edid_get_serial_number (CcmEdid *edid);
+const gchar *ccm_edid_get_eisa_id (CcmEdid *edid);
+const gchar *ccm_edid_get_checksum (CcmEdid *edid);
+const gchar *ccm_edid_get_pnp_id (CcmEdid *edid);
+guint ccm_edid_get_width (CcmEdid *edid);
+guint ccm_edid_get_height (CcmEdid *edid);
+gfloat ccm_edid_get_gamma (CcmEdid *edid);
+const CdColorYxy *ccm_edid_get_red (CcmEdid *edid);
+const CdColorYxy *ccm_edid_get_green (CcmEdid *edid);
+const CdColorYxy *ccm_edid_get_blue (CcmEdid *edid);
+const CdColorYxy *ccm_edid_get_white (CcmEdid *edid);
+
+G_END_DECLS
+
+#endif /* __CCM_EDID_H */
+
diff --git a/plugins/color/ccm-self-test.c b/plugins/color/ccm-self-test.c
new file mode 100644
index 00000000..1b00697b
--- /dev/null
+++ b/plugins/color/ccm-self-test.c
@@ -0,0 +1,429 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes
+ *
+ * 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
+#include
+#include
+
+#include "ccm-edid.h"
+#include "csd-color-state.h"
+#include "csd-night-light.h"
+#include "csd-night-light-common.h"
+
+GMainLoop *mainloop;
+
+static void
+on_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ guint *cnt = (guint *) user_data;
+ (*cnt)++;
+}
+
+static gboolean
+quit_mainloop (gpointer user_data)
+{
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
+static void
+ccm_test_night_light (void)
+{
+ gboolean ret;
+ guint active_cnt = 0;
+ 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(CsdNightLight) nlight = NULL;
+ g_autoptr(GSettings) settings = NULL;
+
+ nlight = csd_night_light_new ();
+ g_assert (CSD_IS_NIGHT_LIGHT (nlight));
+ g_signal_connect (nlight, "notify::active",
+ G_CALLBACK (on_notify), &active_cnt);
+ 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);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+
+ /* do not smooth the transition */
+ csd_night_light_set_smooth_enabled (nlight, FALSE);
+
+ /* switch off */
+ settings = g_settings_new ("org.gnome.settings-daemon.plugins.color");
+ g_settings_set_boolean (settings, "night-light-schedule-automatic", FALSE);
+ g_settings_set_boolean (settings, "night-light-enabled", FALSE);
+ g_settings_set_uint (settings, "night-light-temperature", 4000);
+
+ /* check default values */
+ g_assert (!csd_night_light_get_active (nlight));
+ g_assert_cmpint ((gint) csd_night_light_get_sunrise (nlight), ==, -1);
+ g_assert_cmpint ((gint) csd_night_light_get_sunset (nlight), ==, -1);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (!csd_night_light_get_disabled_until_tmw (nlight));
+
+ /* start module, disabled */
+ ret = csd_night_light_start (nlight, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert (!csd_night_light_get_active (nlight));
+ g_assert_cmpint (active_cnt, ==, 0);
+ 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, "night-light-last-coordinates",
+ g_variant_new ("(dd)", 51.5, -0.1278));
+ g_settings_set_boolean (settings, "night-light-schedule-automatic", TRUE);
+ g_settings_set_boolean (settings, "night-light-enabled", TRUE);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert_cmpint (active_cnt, ==, 1);
+ 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) csd_night_light_get_sunrise (nlight), ==, 7);
+ g_assert_cmpint ((gint) csd_night_light_get_sunset (nlight), ==, 17);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, 4000);
+ g_assert (!csd_night_light_get_disabled_until_tmw (nlight));
+
+ /* disable for one day */
+ csd_night_light_set_disabled_until_tmw (nlight, TRUE);
+ csd_night_light_set_disabled_until_tmw (nlight, TRUE);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert (csd_night_light_get_disabled_until_tmw (nlight));
+ g_assert_cmpint (temperature_cnt, ==, 2);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 1);
+
+ /* change our mind */
+ csd_night_light_set_disabled_until_tmw (nlight, FALSE);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, 4000);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert (!csd_night_light_get_disabled_until_tmw (nlight));
+ g_assert_cmpint (active_cnt, ==, 1);
+ g_assert_cmpint (temperature_cnt, ==, 3);
+ g_assert_cmpint (disabled_until_tmw_cnt, ==, 2);
+
+ /* enabled manual mode (night shift) */
+ g_settings_set_double (settings, "night-light-schedule-from", 4.0);
+ g_settings_set_double (settings, "night-light-schedule-to", 16.f);
+ g_settings_set_boolean (settings, "night-light-schedule-automatic", FALSE);
+ g_assert_cmpint (active_cnt, ==, 2);
+ 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 (!csd_night_light_get_active (nlight));
+ g_assert_cmpint ((gint) csd_night_light_get_sunrise (nlight), ==, 7);
+ g_assert_cmpint ((gint) csd_night_light_get_sunset (nlight), ==, 17);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+ g_assert (!csd_night_light_get_disabled_until_tmw (nlight));
+
+ /* disable, with no changes */
+ g_settings_set_boolean (settings, "night-light-enabled", FALSE);
+ g_assert (!csd_night_light_get_active (nlight));
+ g_assert_cmpint (active_cnt, ==, 2);
+ 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);
+
+
+ /* Finally, check that cancelling a smooth transition works */
+ csd_night_light_set_smooth_enabled (nlight, TRUE);
+ /* Enable night light and automatic scheduling */
+ g_settings_set_boolean (settings, "night-light-schedule-automatic", TRUE);
+ g_settings_set_boolean (settings, "night-light-enabled", TRUE);
+ /* It should be active again, and a smooth transition is being done,
+ * so the color temperature is still the default at this point. */
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+ /* Turn off immediately, before the first timeout event is fired. */
+ g_settings_set_boolean (settings, "night-light-schedule-automatic", FALSE);
+ g_settings_set_boolean (settings, "night-light-enabled", FALSE);
+ g_assert (!csd_night_light_get_active (nlight));
+
+ /* Now, sleep for a bit (the smooth transition time is 5 seconds) */
+ g_timeout_add (5000, quit_mainloop, NULL);
+ g_main_loop_run (mainloop);
+
+ /* Ensure that the color temperature is still the default one.*/
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+
+ /* Check that disabled until tomorrow resets again correctly. */
+ g_settings_set_double (settings, "night-light-schedule-from", 17.0);
+ g_settings_set_double (settings, "night-light-schedule-to", 7.f);
+ g_settings_set_boolean (settings, "night-light-enabled", TRUE);
+ csd_night_light_set_disabled_until_tmw (nlight, TRUE);
+
+ /* Move time past midnight */
+ g_clear_pointer (&datetime_override, g_date_time_unref);
+ datetime_override = g_date_time_new_utc (2017, 2, 9, 1, 0, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_true (csd_night_light_get_disabled_until_tmw (nlight));
+
+ /* Move past sunrise */
+ g_clear_pointer (&datetime_override, g_date_time_unref);
+ datetime_override = g_date_time_new_utc (2017, 2, 9, 8, 0, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_false (csd_night_light_get_disabled_until_tmw (nlight));
+
+ csd_night_light_set_disabled_until_tmw (nlight, TRUE);
+
+ /* Move into night more than 24h in the future */
+ g_clear_pointer (&datetime_override, g_date_time_unref);
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 20, 0, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_false (csd_night_light_get_disabled_until_tmw (nlight));
+
+
+ /* Check that we are always in night mode if from/to are equal. */
+ csd_night_light_set_smooth_enabled (nlight, FALSE);
+ g_settings_set_double (settings, "night-light-schedule-from", 6.0);
+ g_settings_set_double (settings, "night-light-schedule-to", 6.0);
+ g_settings_set_boolean (settings, "night-light-enabled", TRUE);
+
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 5, 50, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, 4000);
+
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 6, 0, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, 4000);
+
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 6, 10, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, 4000);
+
+
+ /* Check that the smearing time is lowered correctly when the times are close. */
+ g_settings_set_double (settings, "night-light-schedule-from", 6.0);
+ g_settings_set_double (settings, "night-light-schedule-to", 6.1);
+
+ /* Not enabled 10 minutes before sunset */
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 5, 50, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_false (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+ /* Not enabled >10 minutes after sunrise */
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 6, 20, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_false (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), ==, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+ /* ~50% smeared 3 min before sunrise (sunrise at 6 past) */
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 6, 3, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_true (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), <=, (CSD_COLOR_TEMPERATURE_DEFAULT + 4000) / 2 + 20);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), >=, (CSD_COLOR_TEMPERATURE_DEFAULT + 4000) / 2 - 20);
+
+ /* ~50% smeared 3 min before sunset (sunset at 6 past) */
+ g_settings_set_double (settings, "night-light-schedule-from", 6.1);
+ g_settings_set_double (settings, "night-light-schedule-to", 6.0);
+ datetime_override = g_date_time_new_utc (2017, 2, 10, 6, 3, 0);
+ csd_night_light_set_date_time_now (nlight, datetime_override);
+ g_assert_true (csd_night_light_get_active (nlight));
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), <=, (CSD_COLOR_TEMPERATURE_DEFAULT + 4000) / 2 + 20);
+ g_assert_cmpint (csd_night_light_get_temperature (nlight), >=, (CSD_COLOR_TEMPERATURE_DEFAULT + 4000) / 2 - 20);
+}
+
+static const gboolean
+ccm_vendor_is_goldstar (const char * const vendor) {
+ if (g_strcmp0 (vendor, "Goldstar Company Ltd") == 0)
+ return TRUE;
+ /* Goldstar was changed to LG in hwdb (systemd) 240.
+ * https://github.com/systemd/systemd/commit/c6d7a5e9a3836f8
+ */
+ if (g_strcmp0 (vendor, "LG Electronics") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static void
+ccm_test_edid_func (void)
+{
+ CcmEdid *edid;
+ gchar *data;
+ gboolean ret;
+ GError *error = NULL;
+ gsize length = 0;
+
+ edid = ccm_edid_new ();
+ g_assert (edid != NULL);
+
+ /* LG 21" LCD panel */
+ ret = g_file_get_contents (TESTDATADIR "/LG-L225W-External.bin",
+ &data, &length, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ ret = ccm_edid_parse (edid, (const guint8 *) data, length, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpstr (ccm_edid_get_monitor_name (edid), ==, "L225W");
+ g_printerr ("ff: %s\n", ccm_edid_get_vendor_name (edid));
+ g_assert_true (ccm_vendor_is_goldstar (ccm_edid_get_vendor_name (edid)));
+ g_assert_cmpstr (ccm_edid_get_serial_number (edid), ==, "34398");
+ g_assert_cmpstr (ccm_edid_get_eisa_id (edid), ==, NULL);
+ g_assert_cmpstr (ccm_edid_get_checksum (edid), ==, "0bb44865bb29984a4bae620656c31368");
+ g_assert_cmpstr (ccm_edid_get_pnp_id (edid), ==, "GSM");
+ g_assert_cmpint (ccm_edid_get_height (edid), ==, 30);
+ g_assert_cmpint (ccm_edid_get_width (edid), ==, 47);
+ g_assert_cmpfloat (ccm_edid_get_gamma (edid), >=, 2.2f - 0.01);
+ g_assert_cmpfloat (ccm_edid_get_gamma (edid), <, 2.2f + 0.01);
+ g_free (data);
+
+ /* Lenovo T61 internal Panel */
+ ret = g_file_get_contents (TESTDATADIR "/Lenovo-T61-Internal.bin",
+ &data, &length, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ ret = ccm_edid_parse (edid, (const guint8 *) data, length, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpstr (ccm_edid_get_monitor_name (edid), ==, NULL);
+ g_assert_cmpstr (ccm_edid_get_vendor_name (edid), ==, "IBM Brasil");
+ g_assert_cmpstr (ccm_edid_get_serial_number (edid), ==, NULL);
+ g_assert_cmpstr (ccm_edid_get_eisa_id (edid), ==, "LTN154P2-L05");
+ g_assert_cmpstr (ccm_edid_get_checksum (edid), ==, "e1865128c7cd5e5ed49ecfc8102f6f9c");
+ g_assert_cmpstr (ccm_edid_get_pnp_id (edid), ==, "IBM");
+ g_assert_cmpint (ccm_edid_get_height (edid), ==, 21);
+ g_assert_cmpint (ccm_edid_get_width (edid), ==, 33);
+ g_assert_cmpfloat (ccm_edid_get_gamma (edid), >=, 2.2f - 0.01);
+ g_assert_cmpfloat (ccm_edid_get_gamma (edid), <, 2.2f + 0.01);
+ g_free (data);
+
+ g_object_unref (edid);
+}
+
+static void
+ccm_test_sunset_sunrise (void)
+{
+ gdouble sunrise;
+ gdouble sunrise_actual = 7.6;
+ gdouble sunset;
+ gdouble sunset_actual = 16.8;
+ g_autoptr(GDateTime) dt = g_date_time_new_utc (2007, 2, 1, 0, 0, 0);
+
+ /* get for London, today */
+ csd_night_light_get_sunrise_sunset (dt, 51.5, -0.1278, &sunrise, &sunset);
+ g_assert_cmpfloat (sunrise, <, sunrise_actual + 0.1);
+ g_assert_cmpfloat (sunrise, >, sunrise_actual - 0.1);
+ g_assert_cmpfloat (sunset, <, sunset_actual + 0.1);
+ g_assert_cmpfloat (sunset, >, sunset_actual - 0.1);
+}
+
+static void
+ccm_test_sunset_sunrise_fractional_timezone (void)
+{
+ gdouble sunrise;
+ gdouble sunrise_actual = 7.6 + 1.5;
+ gdouble sunset;
+ gdouble sunset_actual = 16.8 + 1.5;
+ g_autoptr(GTimeZone) tz = NULL;
+ g_autoptr(GDateTime) dt = NULL;
+
+ tz = g_time_zone_new ("+01:30");
+ dt = g_date_time_new (tz, 2007, 2, 1, 0, 0, 0);
+
+ /* get for our made up timezone, today */
+ csd_night_light_get_sunrise_sunset (dt, 51.5, -0.1278, &sunrise, &sunset);
+ g_assert_cmpfloat (sunrise, <, sunrise_actual + 0.1);
+ g_assert_cmpfloat (sunrise, >, sunrise_actual - 0.1);
+ g_assert_cmpfloat (sunset, <, sunset_actual + 0.1);
+ g_assert_cmpfloat (sunset, >, sunset_actual - 0.1);
+}
+
+static void
+ccm_test_frac_day (void)
+{
+ g_autoptr(GDateTime) dt = g_date_time_new_utc (2007, 2, 1, 12, 59, 59);
+ gdouble fd;
+ gdouble fd_actual = 12.99;
+
+ /* test for 12:59:59 */
+ fd = csd_night_light_frac_day_from_dt (dt);
+ g_assert_cmpfloat (fd, >, fd_actual - 0.01);
+ g_assert_cmpfloat (fd, <, fd_actual + 0.01);
+
+ /* test same day */
+ g_assert_true (csd_night_light_frac_day_is_between (12, 6, 20));
+ g_assert_false (csd_night_light_frac_day_is_between (5, 6, 20));
+ g_assert_true (csd_night_light_frac_day_is_between (12, 0, 24));
+ g_assert_true (csd_night_light_frac_day_is_between (12, -1, 25));
+
+ /* test rollover to next day */
+ g_assert_true (csd_night_light_frac_day_is_between (23, 20, 6));
+ g_assert_false (csd_night_light_frac_day_is_between (12, 20, 6));
+
+ /* test rollover to the previous day */
+ g_assert_true (csd_night_light_frac_day_is_between (5, 16, 8));
+
+ /* test equality */
+ g_assert_true (csd_night_light_frac_day_is_between (12, 0.5, 24.5));
+ g_assert_true (csd_night_light_frac_day_is_between (0.5, 0.5, 0.5));
+}
+
+int
+main (int argc, char **argv)
+{
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+
+ g_test_init (&argc, &argv, NULL);
+
+ mainloop = g_main_loop_new (g_main_context_default (), FALSE);
+
+ g_test_add_func ("/color/edid", ccm_test_edid_func);
+ g_test_add_func ("/color/sunset-sunrise", ccm_test_sunset_sunrise);
+ g_test_add_func ("/color/sunset-sunrise/fractional-timezone", ccm_test_sunset_sunrise_fractional_timezone);
+ g_test_add_func ("/color/fractional-day", ccm_test_frac_day);
+ g_test_add_func ("/color/night-light", ccm_test_night_light);
+
+ return g_test_run ();
+}
+
diff --git a/plugins/color/csd-color-calibrate.c b/plugins/color/csd-color-calibrate.c
new file mode 100644
index 00000000..a6872605
--- /dev/null
+++ b/plugins/color/csd-color-calibrate.c
@@ -0,0 +1,412 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include "csd-color-calibrate.h"
+
+#define CCM_SESSION_NOTIFY_TIMEOUT 30000 /* ms */
+#define CCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD "recalibrate-printer-threshold"
+#define CCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD "recalibrate-display-threshold"
+
+struct _CsdColorCalibrate
+{
+ GObject parent;
+
+ CdClient *client;
+ GSettings *settings;
+};
+
+static void csd_color_calibrate_class_init (CsdColorCalibrateClass *klass);
+static void csd_color_calibrate_init (CsdColorCalibrate *color_calibrate);
+static void csd_color_calibrate_finalize (GObject *object);
+
+G_DEFINE_TYPE (CsdColorCalibrate, csd_color_calibrate, G_TYPE_OBJECT)
+
+typedef struct {
+ CsdColorCalibrate *calibrate;
+ CdProfile *profile;
+ CdDevice *device;
+ guint32 output_id;
+} CcmSessionAsyncHelper;
+
+static void
+ccm_session_async_helper_free (CcmSessionAsyncHelper *helper)
+{
+ if (helper->calibrate != NULL)
+ g_object_unref (helper->calibrate);
+ if (helper->profile != NULL)
+ g_object_unref (helper->profile);
+ if (helper->device != NULL)
+ g_object_unref (helper->device);
+ g_free (helper);
+}
+
+static void
+ccm_session_exec_control_center (CsdColorCalibrate *calibrate)
+{
+ gboolean ret;
+ GError *error = NULL;
+ GAppInfo *app_info;
+ GdkAppLaunchContext *launch_context;
+
+ /* setup the launch context so the startup notification is correct */
+ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ());
+ app_info = g_app_info_create_from_commandline (BINDIR "/gnome-control-center color",
+ "gnome-control-center",
+ G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
+ &error);
+ if (app_info == NULL) {
+ g_warning ("failed to create application info: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* launch gnome-control-center */
+ ret = g_app_info_launch (app_info,
+ NULL,
+ G_APP_LAUNCH_CONTEXT (launch_context),
+ &error);
+ if (!ret) {
+ g_warning ("failed to launch gnome-control-center: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+out:
+ g_object_unref (launch_context);
+ if (app_info != NULL)
+ g_object_unref (app_info);
+}
+
+static void
+ccm_session_notify_cb (NotifyNotification *notification,
+ gchar *action,
+ gpointer user_data)
+{
+ CsdColorCalibrate *calibrate = CSD_COLOR_CALIBRATE (user_data);
+
+ if (g_strcmp0 (action, "recalibrate") == 0) {
+ notify_notification_close (notification, NULL);
+ ccm_session_exec_control_center (calibrate);
+ }
+}
+
+static void
+closed_cb (NotifyNotification *notification, gpointer data)
+{
+ g_object_unref (notification);
+}
+
+static gboolean
+ccm_session_notify_recalibrate (CsdColorCalibrate *calibrate,
+ const gchar *title,
+ const gchar *message,
+ CdDeviceKind kind)
+{
+ gboolean ret;
+ GError *error = NULL;
+ NotifyNotification *notification;
+
+ /* show a bubble */
+ notification = notify_notification_new (title, message, "preferences-color");
+ notify_notification_set_timeout (notification, CCM_SESSION_NOTIFY_TIMEOUT);
+ notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+ notify_notification_set_app_name (notification, _("Color"));
+ notify_notification_set_hint_string (notification, "desktop-entry", "gnome-color-panel");
+
+ notify_notification_add_action (notification,
+ "recalibrate",
+ /* TRANSLATORS: button: this is to open CCM */
+ _("Recalibrate now"),
+ ccm_session_notify_cb,
+ calibrate, NULL);
+
+ g_signal_connect (notification, "closed", G_CALLBACK (closed_cb), NULL);
+ ret = notify_notification_show (notification, &error);
+ if (!ret) {
+ g_warning ("failed to show notification: %s",
+ error->message);
+ g_error_free (error);
+ }
+ return ret;
+}
+
+static gchar *
+ccm_session_device_get_title (CdDevice *device)
+{
+ const gchar *vendor;
+ const gchar *model;
+
+ model = cd_device_get_model (device);
+ vendor = cd_device_get_vendor (device);
+ if (model != NULL && vendor != NULL)
+ return g_strdup_printf ("%s - %s", vendor, model);
+ if (vendor != NULL)
+ return g_strdup (vendor);
+ if (model != NULL)
+ return g_strdup (model);
+ return g_strdup (cd_device_get_id (device));
+}
+
+static void
+ccm_session_notify_device (CsdColorCalibrate *calibrate, CdDevice *device)
+{
+ CdDeviceKind kind;
+ const gchar *title;
+ gchar *device_title = NULL;
+ gchar *message;
+ guint threshold;
+ glong since;
+
+ /* TRANSLATORS: this is when the device has not been recalibrated in a while */
+ title = _("Recalibration required");
+ device_title = ccm_session_device_get_title (device);
+
+ /* check we care */
+ kind = cd_device_get_kind (device);
+ if (kind == CD_DEVICE_KIND_DISPLAY) {
+
+ /* get from GSettings */
+ threshold = g_settings_get_uint (calibrate->settings,
+ CCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD);
+
+ /* TRANSLATORS: this is when the display has not been recalibrated in a while */
+ message = g_strdup_printf (_("The display “%s” should be recalibrated soon."),
+ device_title);
+ } else {
+
+ /* get from GSettings */
+ threshold = g_settings_get_uint (calibrate->settings,
+ CCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD);
+
+ /* TRANSLATORS: this is when the printer has not been recalibrated in a while */
+ message = g_strdup_printf (_("The printer “%s” should be recalibrated soon."),
+ device_title);
+ }
+
+ /* check if we need to notify */
+ since = (g_get_real_time () - cd_device_get_modified (device)) / G_USEC_PER_SEC;
+ if (threshold > since)
+ ccm_session_notify_recalibrate (calibrate, title, message, kind);
+ g_free (device_title);
+ g_free (message);
+}
+
+static void
+ccm_session_profile_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ const gchar *filename;
+ gboolean ret;
+ gchar *basename = NULL;
+ const gchar *data_source;
+ GError *error = NULL;
+ CdProfile *profile = CD_PROFILE (object);
+ CcmSessionAsyncHelper *helper = (CcmSessionAsyncHelper *) user_data;
+ CsdColorCalibrate *calibrate = CSD_COLOR_CALIBRATE (helper->calibrate);
+
+ ret = cd_profile_connect_finish (profile,
+ res,
+ &error);
+ if (!ret) {
+ g_warning ("failed to connect to profile: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* ensure it's a profile generated by us */
+ data_source = cd_profile_get_metadata_item (profile,
+ CD_PROFILE_METADATA_DATA_SOURCE);
+ if (data_source == NULL) {
+
+ /* existing profiles from gnome-color-calibrate < 3.1
+ * won't have the extra metadata values added */
+ filename = cd_profile_get_filename (profile);
+ if (filename == NULL)
+ goto out;
+ basename = g_path_get_basename (filename);
+ if (!g_str_has_prefix (basename, "CCM")) {
+ g_debug ("not a CCM profile for %s: %s",
+ cd_device_get_id (helper->device), filename);
+ goto out;
+ }
+
+ /* ensure it's been created from a calibration, rather than from
+ * auto-EDID */
+ } else if (g_strcmp0 (data_source,
+ CD_PROFILE_METADATA_DATA_SOURCE_CALIB) != 0) {
+ g_debug ("not a calib profile for %s",
+ cd_device_get_id (helper->device));
+ goto out;
+ }
+
+ /* handle device */
+ ccm_session_notify_device (calibrate, helper->device);
+out:
+ ccm_session_async_helper_free (helper);
+ g_free (basename);
+}
+
+static void
+ccm_session_device_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+ CdDeviceKind kind;
+ CdProfile *profile = NULL;
+ CdDevice *device = CD_DEVICE (object);
+ CsdColorCalibrate *calibrate = CSD_COLOR_CALIBRATE (user_data);
+ CcmSessionAsyncHelper *helper;
+
+ ret = cd_device_connect_finish (device,
+ res,
+ &error);
+ if (!ret) {
+ g_warning ("failed to connect to device: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* check we care */
+ kind = cd_device_get_kind (device);
+ if (kind != CD_DEVICE_KIND_DISPLAY &&
+ kind != CD_DEVICE_KIND_PRINTER)
+ goto out;
+
+ /* ensure we have a profile */
+ profile = cd_device_get_default_profile (device);
+ if (profile == NULL) {
+ g_debug ("no profile set for %s", cd_device_get_id (device));
+ goto out;
+ }
+
+ /* connect to the profile */
+ helper = g_new0 (CcmSessionAsyncHelper, 1);
+ helper->calibrate = g_object_ref (calibrate);
+ helper->device = g_object_ref (device);
+ cd_profile_connect (profile,
+ NULL,
+ ccm_session_profile_connect_cb,
+ helper);
+out:
+ if (profile != NULL)
+ g_object_unref (profile);
+}
+
+static void
+ccm_session_device_added_notify_cb (CdClient *client,
+ CdDevice *device,
+ CsdColorCalibrate *calibrate)
+{
+ /* connect to the device to get properties */
+ cd_device_connect (device,
+ NULL,
+ ccm_session_device_connect_cb,
+ calibrate);
+}
+
+static void
+ccm_session_sensor_added_cb (CdClient *client,
+ CdSensor *sensor,
+ CsdColorCalibrate *calibrate)
+{
+ ca_context_play (ca_gtk_context_get (), 0,
+ CA_PROP_EVENT_ID, "device-added",
+ /* TRANSLATORS: this is the application name */
+ CA_PROP_APPLICATION_NAME, _("GNOME Settings Daemon Color Plugin"),
+ /* TRANSLATORS: this is a sound description */
+ CA_PROP_EVENT_DESCRIPTION, _("Color calibration device added"), NULL);
+
+ /* open up the color prefs window */
+ ccm_session_exec_control_center (calibrate);
+}
+
+static void
+ccm_session_sensor_removed_cb (CdClient *client,
+ CdSensor *sensor,
+ CsdColorCalibrate *calibrate)
+{
+ ca_context_play (ca_gtk_context_get (), 0,
+ CA_PROP_EVENT_ID, "device-removed",
+ /* TRANSLATORS: this is the application name */
+ CA_PROP_APPLICATION_NAME, _("GNOME Settings Daemon Color Plugin"),
+ /* TRANSLATORS: this is a sound description */
+ CA_PROP_EVENT_DESCRIPTION, _("Color calibration device removed"), NULL);
+}
+
+static void
+csd_color_calibrate_class_init (CsdColorCalibrateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = csd_color_calibrate_finalize;
+}
+
+static void
+csd_color_calibrate_init (CsdColorCalibrate *calibrate)
+{
+ calibrate->settings = g_settings_new ("org.gnome.settings-daemon.plugins.color");
+ calibrate->client = cd_client_new ();
+ g_signal_connect (calibrate->client, "device-added",
+ G_CALLBACK (ccm_session_device_added_notify_cb),
+ calibrate);
+ g_signal_connect (calibrate->client, "sensor-added",
+ G_CALLBACK (ccm_session_sensor_added_cb),
+ calibrate);
+ g_signal_connect (calibrate->client, "sensor-removed",
+ G_CALLBACK (ccm_session_sensor_removed_cb),
+ calibrate);
+}
+
+static void
+csd_color_calibrate_finalize (GObject *object)
+{
+ CsdColorCalibrate *calibrate;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CSD_IS_COLOR_CALIBRATE (object));
+
+ calibrate = CSD_COLOR_CALIBRATE (object);
+
+ g_clear_object (&calibrate->settings);
+ g_clear_object (&calibrate->client);
+
+ G_OBJECT_CLASS (csd_color_calibrate_parent_class)->finalize (object);
+}
+
+CsdColorCalibrate *
+csd_color_calibrate_new (void)
+{
+ CsdColorCalibrate *calibrate;
+ calibrate = g_object_new (CSD_TYPE_COLOR_CALIBRATE, NULL);
+ return CSD_COLOR_CALIBRATE (calibrate);
+}
diff --git a/plugins/color/csd-color-calibrate.h b/plugins/color/csd-color-calibrate.h
new file mode 100644
index 00000000..55c10277
--- /dev/null
+++ b/plugins/color/csd-color-calibrate.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#ifndef __CSD_COLOR_CALIBRATE_H
+#define __CSD_COLOR_CALIBRATE_H
+
+#include
+
+G_BEGIN_DECLS
+
+#define CSD_TYPE_COLOR_CALIBRATE (csd_color_calibrate_get_type ())
+G_DECLARE_FINAL_TYPE (CsdColorCalibrate, csd_color_calibrate, CSD, COLOR_CALIBRATE, GObject)
+
+GType csd_color_calibrate_get_type (void);
+GQuark csd_color_calibrate_error_quark (void);
+
+CsdColorCalibrate * csd_color_calibrate_new (void);
+
+G_END_DECLS
+
+#endif /* __CSD_COLOR_CALIBRATE_H */
diff --git a/plugins/color/csd-color-manager.c b/plugins/color/csd-color-manager.c
index 86a87f6d..936a5cca 100644
--- a/plugins/color/csd-color-manager.c
+++ b/plugins/color/csd-color-manager.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann
- * Copyright (C) 2011 Richard Hughes
+ * Copyright (C) 2011-2013 Richard Hughes
*
* 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
@@ -14,71 +14,75 @@
* 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 - Suite 500, Boston, MA 02110-1335, USA.
+ * along with this program; if not, see .
*
*/
#include "config.h"
#include
-#include
-#include
#include
-#include
-#include
-#include
-
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include
+#include
#include "cinnamon-settings-profile.h"
-#include "cinnamon-settings-session.h"
+#include "csd-color-calibrate.h"
#include "csd-color-manager.h"
-#include "gcm-profile-store.h"
-#include "gcm-dmi.h"
-#include "gcm-edid.h"
-
-#define CSD_COLOR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_COLOR_MANAGER, CsdColorManagerPrivate))
-
-#define GCM_SESSION_NOTIFY_TIMEOUT 30000 /* ms */
-#define GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD "recalibrate-printer-threshold"
-#define GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD "recalibrate-display-threshold"
-
-struct CsdColorManagerPrivate
-{
- CinnamonSettingsSession *session;
- CdClient *client;
- GSettings *settings;
- GcmProfileStore *profile_store;
- GcmDmi *dmi;
- GnomeRRScreen *x11_screen;
- GHashTable *edid_cache;
- GdkWindow *gdk_window;
- CinnamonSettingsSessionState session_state;
- GHashTable *device_assign_hash;
+#include "csd-color-profiles.h"
+#include "csd-color-state.h"
+#include "csd-night-light.h"
+
+#define CSD_DBUS_NAME "org.cinnamon.SettingsDaemon"
+#define CSD_DBUS_PATH "/org/cinnamon/SettingsDaemon"
+#define CSD_DBUS_BASE_INTERFACE "org.cinnamon.SettingsDaemon"
+
+#define CSD_COLOR_DBUS_NAME CSD_DBUS_NAME ".Color"
+#define CSD_COLOR_DBUS_PATH CSD_DBUS_PATH "/Color"
+#define CSD_COLOR_DBUS_INTERFACE CSD_DBUS_BASE_INTERFACE ".Color"
+
+static const gchar introspection_xml[] =
+""
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+"";
+
+struct _CsdColorManager
+{
+ GObject parent;
+
+ /* D-Bus */
+ guint name_id;
+ GDBusNodeInfo *introspection_data;
+ GDBusConnection *connection;
+ GCancellable *bus_cancellable;
+
+ CsdColorCalibrate *calibrate;
+ CsdColorProfiles *profiles;
+ CsdColorState *state;
+ CsdNightLight *nlight;
+
+ guint nlight_forced_timeout_id;
};
enum {
PROP_0,
};
+static void csd_color_manager_class_init (CsdColorManagerClass *klass);
+static void csd_color_manager_init (CsdColorManager *color_manager);
static void csd_color_manager_finalize (GObject *object);
G_DEFINE_TYPE (CsdColorManager, csd_color_manager, G_TYPE_OBJECT)
static gpointer manager_object = NULL;
-/* see http://www.oyranos.org/wiki/index.php?title=ICC_Profiles_in_X_Specification_0.3 */
-#define GCM_ICC_PROFILE_IN_X_VERSION_MAJOR 0
-#define GCM_ICC_PROFILE_IN_X_VERSION_MINOR 3
-
-typedef struct {
- guint32 red;
- guint32 green;
- guint32 blue;
-} GnomeRROutputClutItem;
-
GQuark
csd_color_manager_error_quark (void)
{
@@ -88,2264 +92,398 @@ csd_color_manager_error_quark (void)
return quark;
}
-static GcmEdid *
-gcm_session_get_output_edid (CsdColorManager *manager, GnomeRROutput *output, GError **error)
-{
- const guint8 *data;
- gsize size;
- GcmEdid *edid = NULL;
- gboolean ret;
-
- /* can we find it in the cache */
- edid = g_hash_table_lookup (manager->priv->edid_cache,
- gnome_rr_output_get_name (output));
- if (edid != NULL) {
- g_object_ref (edid);
- goto out;
- }
-
- /* parse edid */
- data = gnome_rr_output_get_edid_data (output, &size);
- if (data == NULL || size == 0) {
- g_set_error_literal (error,
- GNOME_RR_ERROR,
- GNOME_RR_ERROR_UNKNOWN,
- "unable to get EDID for output");
- goto out;
- }
- edid = gcm_edid_new ();
- ret = gcm_edid_parse (edid, data, size, error);
- if (!ret) {
- g_object_unref (edid);
- edid = NULL;
- goto out;
- }
-
- /* add to cache */
- g_hash_table_insert (manager->priv->edid_cache,
- g_strdup (gnome_rr_output_get_name (output)),
- g_object_ref (edid));
-out:
- return edid;
-}
-
-static gboolean
-gcm_session_screen_set_icc_profile (CsdColorManager *manager,
- const gchar *filename,
- GError **error)
+gboolean
+csd_color_manager_start (CsdColorManager *manager,
+ GError **error)
{
gboolean ret;
- gchar *data = NULL;
- gsize length;
- guint version_data;
- CsdColorManagerPrivate *priv = manager->priv;
- g_return_val_if_fail (filename != NULL, FALSE);
+ g_debug ("Starting color manager");
+ cinnamon_settings_profile_start (NULL);
- g_debug ("setting root window ICC profile atom from %s", filename);
+ /* start the device probing */
+ csd_color_state_start (manager->state);
- /* get contents of file */
- ret = g_file_get_contents (filename, &data, &length, error);
+ /* start the profiles collection */
+ ret = csd_color_profiles_start (manager->profiles, error);
if (!ret)
goto out;
-
- /* set profile property */
- gdk_property_change (priv->gdk_window,
- gdk_atom_intern_static_string ("_ICC_PROFILE"),
- gdk_atom_intern_static_string ("CARDINAL"),
- 8,
- GDK_PROP_MODE_REPLACE,
- (const guchar *) data, length);
-
- /* set version property */
- version_data = GCM_ICC_PROFILE_IN_X_VERSION_MAJOR * 100 +
- GCM_ICC_PROFILE_IN_X_VERSION_MINOR * 1;
- gdk_property_change (priv->gdk_window,
- gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION"),
- gdk_atom_intern_static_string ("CARDINAL"),
- 8,
- GDK_PROP_MODE_REPLACE,
- (const guchar *) &version_data, 1);
out:
- g_free (data);
+ cinnamon_settings_profile_end (NULL);
return ret;
}
-static gchar *
-gcm_session_get_output_id (CsdColorManager *manager, GnomeRROutput *output)
-{
- const gchar *name;
- const gchar *serial;
- const gchar *vendor;
- GcmEdid *edid = NULL;
- GString *device_id;
- GError *error = NULL;
-
- /* all output devices are prefixed with this */
- device_id = g_string_new ("xrandr");
-
- /* get the output EDID if possible */
- edid = gcm_session_get_output_edid (manager, output, &error);
- if (edid == NULL) {
- g_debug ("no edid for %s [%s], falling back to connection name",
- gnome_rr_output_get_name (output),
- error->message);
- g_error_free (error);
- g_string_append_printf (device_id,
- "-%s",
- gnome_rr_output_get_name (output));
- goto out;
- }
-
- /* check EDID data is okay to use */
- vendor = gcm_edid_get_vendor_name (edid);
- name = gcm_edid_get_monitor_name (edid);
- serial = gcm_edid_get_serial_number (edid);
- if (vendor == NULL && name == NULL && serial == NULL) {
- g_debug ("edid invalid for %s, falling back to connection name",
- gnome_rr_output_get_name (output));
- g_string_append_printf (device_id,
- "-%s",
- gnome_rr_output_get_name (output));
- goto out;
- }
-
- /* use EDID data */
- if (vendor != NULL)
- g_string_append_printf (device_id, "-%s", vendor);
- if (name != NULL)
- g_string_append_printf (device_id, "-%s", name);
- if (serial != NULL)
- g_string_append_printf (device_id, "-%s", serial);
-out:
- if (edid != NULL)
- g_object_unref (edid);
- return g_string_free (device_id, FALSE);
-}
-
-static GnomeRROutput *
-gcm_session_get_output_by_edid_checksum (GnomeRRScreen *screen,
- const gchar *edid_md5,
- GError **error)
-{
- const guint8 *data;
- gchar *checksum;
- GnomeRROutput *output = NULL;
- GnomeRROutput **outputs;
- gsize size;
- guint i;
-
- outputs = gnome_rr_screen_list_outputs (screen);
- if (outputs == NULL) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "Failed to get outputs");
- goto out;
- }
-
- /* find the output */
- for (i = 0; outputs[i] != NULL && output == NULL; i++) {
-
- /* get edid */
- data = gnome_rr_output_get_edid_data (outputs[i], &size);
- if (data == NULL || size < 0x6c)
- continue;
- checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, 0x6c);
- if (g_strcmp0 (checksum, edid_md5) == 0)
- output = outputs[i];
- g_free (checksum);
- }
- if (output == NULL) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "no connected output with that edid hash");
- }
-out:
- return output;
-}
-
-typedef struct {
- CsdColorManager *manager;
- CdProfile *profile;
- CdDevice *device;
- guint32 output_id;
-} GcmSessionAsyncHelper;
-
-static void
-gcm_session_async_helper_free (GcmSessionAsyncHelper *helper)
+void
+csd_color_manager_stop (CsdColorManager *manager)
{
- if (helper->manager != NULL)
- g_object_unref (helper->manager);
- if (helper->profile != NULL)
- g_object_unref (helper->profile);
- if (helper->device != NULL)
- g_object_unref (helper->device);
- g_free (helper);
+ g_debug ("Stopping color manager");
+ csd_color_state_stop (manager->state);
+ csd_color_profiles_stop (manager->profiles);
}
static void
-gcm_session_profile_assign_add_profile_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+csd_color_manager_class_init (CsdColorManagerClass *klass)
{
- CdDevice *device = CD_DEVICE (object);
- gboolean ret;
- GError *error = NULL;
- GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
-
- /* add the profile to the device */
- ret = cd_device_add_profile_finish (device,
- res,
- &error);
- if (!ret) {
- /* this will fail if the profile is already added */
- g_debug ("failed to assign auto-edid profile to device %s: %s",
- cd_device_get_id (device),
- error->message);
- g_error_free (error);
- goto out;
- }
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- /* phew! */
- g_debug ("successfully assigned %s to %s",
- cd_profile_get_object_path (helper->profile),
- cd_device_get_object_path (device));
-out:
- gcm_session_async_helper_free (helper);
+ object_class->finalize = csd_color_manager_finalize;
}
static void
-gcm_session_profile_assign_device_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+emit_property_changed (CsdColorManager *manager,
+ const gchar *property_name,
+ GVariant *property_value)
{
- CdDevice *device = CD_DEVICE (object);
- gboolean ret;
- GError *error = NULL;
- GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
+ GVariantBuilder builder;
+ GVariantBuilder invalidated_builder;
- /* get properties */
- ret = cd_device_connect_finish (device, res, &error);
- if (!ret) {
- g_warning ("cannot connect to device: %s",
- error->message);
- g_error_free (error);
- gcm_session_async_helper_free (helper);
- goto out;
- }
+ /* not yet connected */
+ if (manager->connection == NULL)
+ return;
- /* add the profile to the device */
- cd_device_add_profile (device,
- CD_DEVICE_RELATION_SOFT,
- helper->profile,
- NULL,
- gcm_session_profile_assign_add_profile_cb,
- helper);
-out:
- 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 (manager->connection,
+ NULL,
+ CSD_COLOR_DBUS_PATH,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ CSD_COLOR_DBUS_INTERFACE,
+ &builder,
+ &invalidated_builder),
+ NULL);
+ g_variant_builder_clear (&builder);
+ g_variant_builder_clear (&invalidated_builder);
+}
+
+static void
+on_active_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+ emit_property_changed (manager, "NightLightActive",
+ g_variant_new_boolean (csd_night_light_get_active (manager->nlight)));
}
static void
-gcm_session_profile_assign_find_device_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+on_sunset_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- CdClient *client = CD_CLIENT (object);
- CdDevice *device = NULL;
- GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
- GError *error = NULL;
-
- device = cd_client_find_device_finish (client,
- res,
- &error);
- if (device == NULL) {
- g_warning ("not found device which should have been added: %s",
- error->message);
- g_error_free (error);
- gcm_session_async_helper_free (helper);
- goto out;
- }
-
- /* get properties */
- cd_device_connect (device,
- NULL,
- gcm_session_profile_assign_device_connect_cb,
- helper);
-out:
- if (device != NULL)
- g_object_unref (device);
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+ emit_property_changed (manager, "Sunset",
+ g_variant_new_double (csd_night_light_get_sunset (manager->nlight)));
}
static void
-gcm_session_profile_assign_profile_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+on_sunrise_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- CdProfile *profile = CD_PROFILE (object);
- const gchar *edid_md5;
- gboolean ret;
- gchar *device_id = NULL;
- GcmSessionAsyncHelper *helper;
- GError *error = NULL;
- GHashTable *metadata = NULL;
- GnomeRROutput *output = NULL;
CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- /* get properties */
- ret = cd_profile_connect_finish (profile, res, &error);
- if (!ret) {
- g_warning ("cannot connect to profile: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* does the profile have EDID metadata? */
- metadata = cd_profile_get_metadata (profile);
- edid_md5 = g_hash_table_lookup (metadata,
- CD_PROFILE_METADATA_EDID_MD5);
- if (edid_md5 == NULL)
- goto out;
-
- /* get the GnomeRROutput for the edid */
- output = gcm_session_get_output_by_edid_checksum (manager->priv->x11_screen,
- edid_md5,
- &error);
- if (output == NULL) {
- g_debug ("edid hash %s ignored: %s",
- edid_md5,
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* get the CdDevice for this ID */
- helper = g_new0 (GcmSessionAsyncHelper, 1);
- helper->manager = g_object_ref (manager);
- helper->profile = g_object_ref (profile);
- device_id = gcm_session_get_output_id (manager, output);
- cd_client_find_device (manager->priv->client,
- device_id,
- NULL,
- gcm_session_profile_assign_find_device_cb,
- helper);
-out:
- g_free (device_id);
- if (metadata != NULL)
- g_hash_table_unref (metadata);
+ emit_property_changed (manager, "Sunrise",
+ g_variant_new_double (csd_night_light_get_sunrise (manager->nlight)));
}
static void
-gcm_session_profile_added_assign_cb (CdClient *client,
- CdProfile *profile,
- CsdColorManager *manager)
+on_disabled_until_tmw_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- cd_profile_connect (profile,
- NULL,
- gcm_session_profile_assign_profile_connect_cb,
- manager);
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+ emit_property_changed (manager, "DisabledUntilTomorrow",
+ g_variant_new_boolean (csd_night_light_get_disabled_until_tmw (manager->nlight)));
}
-static cmsBool
-_cmsWriteTagTextAscii (cmsHPROFILE lcms_profile,
- cmsTagSignature sig,
- const gchar *text)
+static void
+on_temperature_notify (CsdNightLight *nlight,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- cmsBool ret;
- cmsMLU *mlu = cmsMLUalloc (0, 1);
- cmsMLUsetASCII (mlu, "EN", "us", text);
- ret = cmsWriteTag (lcms_profile, sig, mlu);
- cmsMLUfree (mlu);
- return ret;
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+ gdouble temperature = csd_night_light_get_temperature (manager->nlight);
+ csd_color_state_set_temperature (manager->state, temperature);
+ emit_property_changed (manager, "Temperature",
+ g_variant_new_double (temperature));
}
-static gboolean
-gcm_utils_mkdir_for_filename (const gchar *filename, GError **error)
+static void
+csd_color_manager_init (CsdColorManager *manager)
{
- gboolean ret = FALSE;
- GFile *file;
- GFile *parent_dir = NULL;
-
- /* get parent directory */
- file = g_file_new_for_path (filename);
- parent_dir = g_file_get_parent (file);
- if (parent_dir == NULL) {
- g_set_error (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "could not get parent dir %s",
- filename);
- goto out;
- }
+ /* setup calibration features */
+ manager->calibrate = csd_color_calibrate_new ();
+ manager->profiles = csd_color_profiles_new ();
+ manager->state = csd_color_state_new ();
- /* ensure destination does not already exist */
- ret = g_file_query_exists (parent_dir, NULL);
- if (ret)
- goto out;
- ret = g_file_make_directory_with_parents (parent_dir, NULL, error);
- if (!ret)
- goto out;
-out:
- if (file != NULL)
- g_object_unref (file);
- if (parent_dir != NULL)
- g_object_unref (parent_dir);
- return ret;
+ /* night light features */
+ manager->nlight = csd_night_light_new ();
+ g_signal_connect (manager->nlight, "notify::active",
+ G_CALLBACK (on_active_notify), manager);
+ g_signal_connect (manager->nlight, "notify::sunset",
+ G_CALLBACK (on_sunset_notify), manager);
+ g_signal_connect (manager->nlight, "notify::sunrise",
+ G_CALLBACK (on_sunrise_notify), manager);
+ g_signal_connect (manager->nlight, "notify::temperature",
+ G_CALLBACK (on_temperature_notify), manager);
+ g_signal_connect (manager->nlight, "notify::disabled-until-tmw",
+ G_CALLBACK (on_disabled_until_tmw_notify), manager);
}
-#ifdef HAVE_NEW_LCMS
-static wchar_t *
-utf8_to_wchar_t (const char *src)
+static void
+csd_color_manager_finalize (GObject *object)
{
- size_t len;
- size_t converted;
- wchar_t *buf = NULL;
+ CsdColorManager *manager;
- len = mbstowcs (NULL, src, 0);
- if (len == (size_t) -1) {
- g_warning ("Invalid UTF-8 in string %s", src);
- goto out;
- }
- len += 1;
- buf = g_malloc (sizeof (wchar_t) * len);
- converted = mbstowcs (buf, src, len - 1);
- g_assert (converted != (size_t)-1);
- buf[converted] = '\0';
-out:
- return buf;
-}
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CSD_IS_COLOR_MANAGER (object));
-static cmsBool
-_cmsDictAddEntryAscii (cmsHANDLE dict,
- const gchar *key,
- const gchar *value)
-{
- cmsBool ret = FALSE;
- wchar_t *mb_key = NULL;
- wchar_t *mb_value = NULL;
-
- mb_key = utf8_to_wchar_t (key);
- if (mb_key == NULL)
- goto out;
- mb_value = utf8_to_wchar_t (value);
- if (mb_value == NULL)
- goto out;
- ret = cmsDictAddEntry (dict, mb_key, mb_value, NULL, NULL);
-out:
- g_free (mb_key);
- g_free (mb_value);
- return ret;
-}
-#endif /* HAVE_NEW_LCMS */
+ manager = CSD_COLOR_MANAGER (object);
-static gboolean
-gcm_apply_create_icc_profile_for_edid (CsdColorManager *manager,
- GcmEdid *edid,
- const gchar *filename,
- GError **error)
-{
- const CdColorYxy *tmp;
- cmsCIExyYTRIPLE chroma;
- cmsCIExyY white_point;
- cmsHPROFILE lcms_profile = NULL;
- cmsToneCurve *transfer_curve[3] = { NULL, NULL, NULL };
- const gchar *data;
- gboolean ret = FALSE;
- gchar *str;
- gfloat edid_gamma;
- gfloat localgamma;
-#ifdef HAVE_NEW_LCMS
- cmsHANDLE dict = NULL;
-#endif
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* ensure the per-user directory exists */
- ret = gcm_utils_mkdir_for_filename (filename, error);
- if (!ret)
- goto out;
+ csd_color_manager_stop (manager);
- /* copy color data from our structures */
- tmp = gcm_edid_get_red (edid);
- chroma.Red.x = tmp->x;
- chroma.Red.y = tmp->y;
- tmp = gcm_edid_get_green (edid);
- chroma.Green.x = tmp->x;
- chroma.Green.y = tmp->y;
- tmp = gcm_edid_get_blue (edid);
- chroma.Blue.x = tmp->x;
- chroma.Blue.y = tmp->y;
- tmp = gcm_edid_get_white (edid);
- white_point.x = tmp->x;
- white_point.y = tmp->y;
- white_point.Y = 1.0;
-
- /* estimate the transfer function for the gamma */
- localgamma = gcm_edid_get_gamma (edid);
- transfer_curve[0] = transfer_curve[1] = transfer_curve[2] = cmsBuildGamma (NULL, localgamma);
-
- /* create our generated profile */
- lcms_profile = cmsCreateRGBProfile (&white_point, &chroma, transfer_curve);
- if (lcms_profile == NULL) {
- g_set_error (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to create profile");
- goto out;
+ if (manager->bus_cancellable != NULL) {
+ g_cancellable_cancel (manager->bus_cancellable);
+ g_clear_object (&manager->bus_cancellable);
}
- cmsSetColorSpace (lcms_profile, cmsSigRgbData);
- cmsSetPCS (lcms_profile, cmsSigXYZData);
- cmsSetHeaderRenderingIntent (lcms_profile, INTENT_PERCEPTUAL);
- cmsSetDeviceClass (lcms_profile, cmsSigDisplayClass);
-
- /* copyright */
- ret = _cmsWriteTagTextAscii (lcms_profile,
- cmsSigCopyrightTag,
- /* deliberately not translated */
- "This profile is free of known copyright restrictions.");
- if (!ret) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to write copyright");
- goto out;
- }
+ g_clear_pointer (&manager->introspection_data, g_dbus_node_info_unref);
+ g_clear_object (&manager->connection);
- /* set model */
- data = gcm_edid_get_monitor_name (edid);
- if (data == NULL)
- data = gcm_dmi_get_name (priv->dmi);
- if (data == NULL)
- data = "Unknown monitor";
- ret = _cmsWriteTagTextAscii (lcms_profile,
- cmsSigDeviceModelDescTag,
- data);
- if (!ret) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to write model");
- goto out;
+ if (manager->name_id != 0) {
+ g_bus_unown_name (manager->name_id);
+ manager->name_id = 0;
}
- /* write title */
- ret = _cmsWriteTagTextAscii (lcms_profile,
- cmsSigProfileDescriptionTag,
- data);
- if (!ret) {
- g_set_error_literal (error, CSD_COLOR_MANAGER_ERROR, CSD_COLOR_MANAGER_ERROR_FAILED, "failed to write description");
- goto out;
- }
+ if (manager->nlight_forced_timeout_id)
+ g_source_remove (manager->nlight_forced_timeout_id);
- /* get manufacturer */
- data = gcm_edid_get_vendor_name (edid);
- if (data == NULL)
- data = gcm_dmi_get_vendor (priv->dmi);
- if (data == NULL)
- data = "Unknown vendor";
- ret = _cmsWriteTagTextAscii (lcms_profile,
- cmsSigDeviceMfgDescTag,
- data);
- if (!ret) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to write manufacturer");
- goto out;
- }
+ g_clear_object (&manager->calibrate);
+ g_clear_object (&manager->profiles);
+ g_clear_object (&manager->state);
+ g_clear_object (&manager->nlight);
-#ifdef HAVE_NEW_LCMS
- /* just create a new dict */
- dict = cmsDictAlloc (NULL);
-
- /* set the framework creator metadata */
- _cmsDictAddEntryAscii (dict,
- CD_PROFILE_METADATA_CMF_PRODUCT,
- PACKAGE_NAME);
- _cmsDictAddEntryAscii (dict,
- CD_PROFILE_METADATA_CMF_BINARY,
- PACKAGE_NAME);
- _cmsDictAddEntryAscii (dict,
- CD_PROFILE_METADATA_CMF_VERSION,
- PACKAGE_VERSION);
-
- /* set the data source so we don't ever prompt the user to
- * recalibrate (as the EDID data won't have changed) */
- _cmsDictAddEntryAscii (dict,
- CD_PROFILE_METADATA_DATA_SOURCE,
- CD_PROFILE_METADATA_DATA_SOURCE_EDID);
-
- /* set 'ICC meta Tag for Monitor Profiles' data */
- _cmsDictAddEntryAscii (dict, "EDID_md5", gcm_edid_get_checksum (edid));
- data = gcm_edid_get_monitor_name (edid);
- if (data != NULL)
- _cmsDictAddEntryAscii (dict, "EDID_model", data);
- data = gcm_edid_get_serial_number (edid);
- if (data != NULL)
- _cmsDictAddEntryAscii (dict, "EDID_serial", data);
- data = gcm_edid_get_pnp_id (edid);
- if (data != NULL)
- _cmsDictAddEntryAscii (dict, "EDID_mnft", data);
- data = gcm_edid_get_vendor_name (edid);
- if (data != NULL)
- _cmsDictAddEntryAscii (dict, "EDID_manufacturer", data);
- edid_gamma = gcm_edid_get_gamma (edid);
- if (edid_gamma > 0.0 && edid_gamma < 10.0) {
- str = g_strdup_printf ("%f", edid_gamma);
- _cmsDictAddEntryAscii (dict, "EDID_gamma", str);
- g_free (str);
- }
+ G_OBJECT_CLASS (csd_color_manager_parent_class)->finalize (object);
+}
- /* also add the primaries */
- str = g_strdup_printf ("%f", chroma.Red.x);
- _cmsDictAddEntryAscii (dict, "EDID_red_x", str);
- g_free (str);
- str = g_strdup_printf ("%f", chroma.Red.y);
- _cmsDictAddEntryAscii (dict, "EDID_red_y", str);
- g_free (str);
- str = g_strdup_printf ("%f", chroma.Green.x);
- _cmsDictAddEntryAscii (dict, "EDID_green_x", str);
- g_free (str);
- str = g_strdup_printf ("%f", chroma.Green.y);
- _cmsDictAddEntryAscii (dict, "EDID_green_y", str);
- g_free (str);
- str = g_strdup_printf ("%f", chroma.Blue.x);
- _cmsDictAddEntryAscii (dict, "EDID_blue_x", str);
- g_free (str);
- str = g_strdup_printf ("%f", chroma.Blue.y);
- _cmsDictAddEntryAscii (dict, "EDID_blue_y", str);
- g_free (str);
-
- /* write new tag */
- ret = cmsWriteTag (lcms_profile, cmsSigMetaTag, dict);
- if (!ret) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to write profile metadata");
- goto out;
- }
-#endif /* HAVE_NEW_LCMS */
-
- /* write profile id */
- ret = cmsMD5computeID (lcms_profile);
- if (!ret) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to write profile id");
- goto out;
- }
+static gboolean
+nlight_forced_timeout_cb (gpointer user_data)
+{
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
- /* save, TODO: get error */
- cmsSaveProfileToFile (lcms_profile, filename);
- ret = TRUE;
-out:
-#ifdef HAVE_NEW_LCMS
- if (dict != NULL)
- cmsDictFree (dict);
-#endif
- if (*transfer_curve != NULL)
- cmsFreeToneCurve (*transfer_curve);
- return ret;
+ manager->nlight_forced_timeout_id = 0;
+ csd_night_light_set_forced (manager->nlight, FALSE);
+
+ return G_SOURCE_REMOVE;
}
-static GPtrArray *
-gcm_session_generate_vcgt (CdProfile *profile, guint size)
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
{
- GnomeRROutputClutItem *tmp;
- GPtrArray *array = NULL;
- const cmsToneCurve **vcgt;
- cmsFloat32Number in;
- guint i;
- const gchar *filename;
- cmsHPROFILE lcms_profile = NULL;
-
- /* invalid size */
- if (size == 0)
- goto out;
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
- /* not an actual profile */
- filename = cd_profile_get_filename (profile);
- if (filename == NULL)
- goto out;
+ if (g_strcmp0 (method_name, "NightLightPreview") == 0) {
+ guint32 duration = 0;
- /* open file */
- lcms_profile = cmsOpenProfileFromFile (filename, "r");
- if (lcms_profile == NULL)
- goto out;
+ if (!manager->nlight) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ "Night-light is currently unavailable");
- /* get tone curves from profile */
- vcgt = cmsReadTag (lcms_profile, cmsSigVcgtTag);
- if (vcgt == NULL || vcgt[0] == NULL) {
- g_debug ("profile does not have any VCGT data");
- goto out;
- }
+ return;
+ }
- /* create array */
- array = g_ptr_array_new_with_free_func (g_free);
- for (i = 0; i < size; i++) {
- in = (gdouble) i / (gdouble) (size - 1);
- tmp = g_new0 (GnomeRROutputClutItem, 1);
- tmp->red = cmsEvalToneCurveFloat(vcgt[0], in) * (gdouble) 0xffff;
- tmp->green = cmsEvalToneCurveFloat(vcgt[1], in) * (gdouble) 0xffff;
- tmp->blue = cmsEvalToneCurveFloat(vcgt[2], in) * (gdouble) 0xffff;
- g_ptr_array_add (array, tmp);
- }
-out:
- if (lcms_profile != NULL)
- cmsCloseProfile (lcms_profile);
- return array;
-}
+ g_variant_get (parameters, "(u)", &duration);
-static guint
-cinnamon_rr_output_get_gamma_size (GnomeRROutput *output)
-{
- GnomeRRCrtc *crtc;
- gint len = 0;
-
- crtc = gnome_rr_output_get_crtc (output);
- if (crtc == NULL)
- return 0;
- gnome_rr_crtc_get_gamma (crtc,
- &len,
- NULL, NULL, NULL);
- return (guint) len;
-}
+ if (duration == 0 || duration > 120) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "Duration is out of the range (0-120].");
-static gboolean
-gcm_session_output_set_gamma (GnomeRROutput *output,
- GPtrArray *array,
- GError **error)
-{
- gboolean ret = TRUE;
- guint16 *red = NULL;
- guint16 *green = NULL;
- guint16 *blue = NULL;
- guint i;
- GnomeRROutputClutItem *data;
- GnomeRRCrtc *crtc;
-
- /* no length? */
- if (array->len == 0) {
- ret = FALSE;
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "no data in the CLUT array");
- goto out;
- }
+ return;
+ }
- /* convert to a type X understands */
- red = g_new (guint16, array->len);
- green = g_new (guint16, array->len);
- blue = g_new (guint16, array->len);
- for (i = 0; i < array->len; i++) {
- data = g_ptr_array_index (array, i);
- red[i] = data->red;
- green[i] = data->green;
- blue[i] = data->blue;
- }
+ if (manager->nlight_forced_timeout_id)
+ g_source_remove (manager->nlight_forced_timeout_id);
+ manager->nlight_forced_timeout_id = g_timeout_add_seconds (duration, nlight_forced_timeout_cb, manager);
- /* send to LUT */
- crtc = gnome_rr_output_get_crtc (output);
- if (crtc == NULL) {
- ret = FALSE;
- g_set_error (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to get ctrc for %s",
- gnome_rr_output_get_name (output));
- goto out;
+ csd_night_light_set_forced (manager->nlight, TRUE);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else {
+ g_assert_not_reached ();
}
- gnome_rr_crtc_set_gamma (crtc, array->len,
- red, green, blue);
-out:
- g_free (red);
- g_free (green);
- g_free (blue);
- return ret;
}
-static gboolean
-gcm_session_device_set_gamma (GnomeRROutput *output,
- CdProfile *profile,
- GError **error)
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error, gpointer user_data)
{
- gboolean ret = FALSE;
- guint size;
- GPtrArray *clut = NULL;
-
- /* create a lookup table */
- size = cinnamon_rr_output_get_gamma_size (output);
- if (size == 0) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "gamma size is zero");
- goto out;
- }
- clut = gcm_session_generate_vcgt (profile, size);
- if (clut == NULL) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to generate vcgt");
- goto out;
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+
+ if (g_strcmp0 (interface_name, CSD_COLOR_DBUS_INTERFACE) != 0) {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "No such interface: %s", interface_name);
+ return NULL;
}
- /* apply the vcgt to this output */
- ret = gcm_session_output_set_gamma (output, clut, error);
- if (!ret)
- goto out;
-out:
- if (clut != NULL)
- g_ptr_array_unref (clut);
- return ret;
-}
+ if (g_strcmp0 (property_name, "NightLightActive") == 0)
+ return g_variant_new_boolean (csd_night_light_get_active (manager->nlight));
-static gboolean
-gcm_session_device_reset_gamma (GnomeRROutput *output,
- GError **error)
-{
- gboolean ret;
- guint i;
- guint size;
- guint32 value;
- GPtrArray *clut;
- GnomeRROutputClutItem *data;
-
- /* create a linear ramp */
- g_debug ("falling back to dummy ramp");
- clut = g_ptr_array_new_with_free_func (g_free);
- size = cinnamon_rr_output_get_gamma_size (output);
- if (size == 0) {
- ret = FALSE;
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "gamma size is zero");
- goto out;
- }
- for (i = 0; i < size; i++) {
- value = (i * 0xffff) / (size - 1);
- data = g_new0 (GnomeRROutputClutItem, 1);
- data->red = value;
- data->green = value;
- data->blue = value;
- g_ptr_array_add (clut, data);
+ if (g_strcmp0 (property_name, "Temperature") == 0) {
+ guint temperature;
+ temperature = csd_color_state_get_temperature (manager->state);
+ return g_variant_new_uint32 (temperature);
}
- /* apply the vcgt to this output */
- ret = gcm_session_output_set_gamma (output, clut, error);
- if (!ret)
- goto out;
-out:
- g_ptr_array_unref (clut);
- return ret;
-}
+ if (g_strcmp0 (property_name, "DisabledUntilTomorrow") == 0)
+ return g_variant_new_boolean (csd_night_light_get_disabled_until_tmw (manager->nlight));
-static GnomeRROutput *
-gcm_session_get_x11_output_by_id (CsdColorManager *manager,
- const gchar *device_id,
- GError **error)
-{
- gchar *output_id;
- GnomeRROutput *output = NULL;
- GnomeRROutput **outputs = NULL;
- guint i;
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* search all X11 outputs for the device id */
- outputs = gnome_rr_screen_list_outputs (priv->x11_screen);
- if (outputs == NULL) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "Failed to get outputs");
- goto out;
- }
- for (i = 0; outputs[i] != NULL && output == NULL; i++) {
- if (!gnome_rr_output_is_connected (outputs[i]))
- continue;
- output_id = gcm_session_get_output_id (manager, outputs[i]);
- if (g_strcmp0 (output_id, device_id) == 0)
- output = outputs[i];
- g_free (output_id);
- }
- if (output == NULL) {
- g_set_error (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "Failed to find output %s",
- device_id);
- }
-out:
- return output;
+ if (g_strcmp0 (property_name, "Sunrise") == 0)
+ return g_variant_new_double (csd_night_light_get_sunrise (manager->nlight));
+
+ if (g_strcmp0 (property_name, "Sunset") == 0)
+ return g_variant_new_double (csd_night_light_get_sunset (manager->nlight));
+
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "Failed to get property: %s", property_name);
+ return NULL;
}
-/* this function is more complicated than it should be, due to the
- * fact that XOrg sometimes assigns no primary devices when using
- * "xrandr --auto" or when the version of RANDR is < 1.3 */
static gboolean
-gcm_session_use_output_profile_for_screen (CsdColorManager *manager,
- GnomeRROutput *output)
+handle_set_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GVariant *value,
+ GError **error, gpointer user_data)
{
- gboolean has_laptop = FALSE;
- gboolean has_primary = FALSE;
- GnomeRROutput **outputs;
- GnomeRROutput *connected = NULL;
- guint i;
-
- /* do we have any screens marked as primary */
- outputs = gnome_rr_screen_list_outputs (manager->priv->x11_screen);
- if (outputs == NULL || outputs[0] == NULL) {
- g_warning ("failed to get outputs");
+ CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
+
+ if (g_strcmp0 (interface_name, CSD_COLOR_DBUS_INTERFACE) != 0) {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "No such interface: %s", interface_name);
return FALSE;
}
- for (i = 0; outputs[i] != NULL; i++) {
- if (!gnome_rr_output_is_connected (outputs[i]))
- continue;
- if (connected == NULL)
- connected = outputs[i];
- if (gnome_rr_output_get_is_primary (outputs[i]))
- has_primary = TRUE;
- if (gnome_rr_output_is_laptop (outputs[i]))
- has_laptop = TRUE;
- }
-
- /* we have an assigned primary device, are we that? */
- if (has_primary)
- return gnome_rr_output_get_is_primary (output);
- /* choosing the internal panel is probably sane */
- if (has_laptop)
- return gnome_rr_output_is_laptop (output);
+ if (g_strcmp0 (property_name, "Temperature") == 0) {
+ guint32 temperature;
+ g_variant_get (value, "u", &temperature);
+ if (temperature < CSD_COLOR_TEMPERATURE_MIN) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "%" G_GUINT32_FORMAT "K is < min %" G_GUINT32_FORMAT "K",
+ temperature, CSD_COLOR_TEMPERATURE_MIN);
+ return FALSE;
+ }
+ if (temperature > CSD_COLOR_TEMPERATURE_MAX) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "%" G_GUINT32_FORMAT "K is > max %" G_GUINT32_FORMAT "K",
+ temperature, CSD_COLOR_TEMPERATURE_MAX);
+ return FALSE;
+ }
+ csd_color_state_set_temperature (manager->state, temperature);
+ return TRUE;
+ }
- /* we have to choose one, so go for the first connected device */
- if (connected != NULL)
- return gnome_rr_output_get_id (connected) == gnome_rr_output_get_id (output);
+ if (g_strcmp0 (property_name, "DisabledUntilTomorrow") == 0) {
+ csd_night_light_set_disabled_until_tmw (manager->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;
}
-/* TODO: remove when we can dep on a released version of colord */
-#ifndef CD_PROFILE_METADATA_SCREEN_BRIGHTNESS
-#define CD_PROFILE_METADATA_SCREEN_BRIGHTNESS "SCREEN_brightness"
-#endif
-
-#define CSD_DBUS_SERVICE "org.cinnamon.SettingsDaemon.Power"
-#define CSD_DBUS_INTERFACE_POWER_SCREEN "org.cinnamon.SettingsDaemon.Power.Screen"
-#define CSD_DBUS_PATH_POWER "/org/cinnamon/SettingsDaemon/Power"
-
-static void
-gcm_session_set_output_percentage_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+static const GDBusInterfaceVTable interface_vtable =
{
- GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
- GError *error = NULL;
- GVariant *retval;
- retval = g_dbus_connection_call_finish (connection,
- res,
- &error);
- if (retval == NULL) {
- g_warning ("failed to set output brightness: %s",
- error->message);
- g_error_free (error);
- return;
- }
- g_variant_unref (retval);
-}
+ handle_method_call,
+ handle_get_property,
+ handle_set_property
+};
static void
-gcm_session_set_output_percentage (guint percentage)
+name_lost_handler_cb (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
- GDBusConnection *connection;
-
- /* get a ref to the existing bus connection */
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- if (connection == NULL)
- return;
- g_dbus_connection_call (connection,
- CSD_DBUS_SERVICE,
- CSD_DBUS_PATH_POWER,
- CSD_DBUS_INTERFACE_POWER_SCREEN,
- "SetPercentage",
- g_variant_new ("(u)", percentage),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL,
- gcm_session_set_output_percentage_cb, NULL);
- g_object_unref (connection);
+ g_debug ("lost name, so exiting");
+ gtk_main_quit ();
}
static void
-gcm_session_device_assign_profile_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+on_bus_gotten (GObject *source_object,
+ GAsyncResult *res,
+ CsdColorManager *manager)
{
- CdProfile *profile = CD_PROFILE (object);
- const gchar *brightness_profile;
- const gchar *filename;
- gboolean ret;
+ GDBusConnection *connection;
GError *error = NULL;
- GnomeRROutput *output;
- guint brightness_percentage;
- GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
- CsdColorManager *manager = CSD_COLOR_MANAGER (helper->manager);
-
- /* get properties */
- ret = cd_profile_connect_finish (profile, res, &error);
- if (!ret) {
- g_warning ("failed to connect to profile: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* get the filename */
- filename = cd_profile_get_filename (profile);
- g_assert (filename != NULL);
-
- /* get the output (can't save in helper as GnomeRROutput isn't
- * a GObject, just a pointer */
- output = gnome_rr_screen_get_output_by_id (manager->priv->x11_screen,
- helper->output_id);
- if (output == NULL)
- goto out;
- /* if output is a laptop screen and the profile has a
- * calibration brightness then set this new brightness */
- brightness_profile = cd_profile_get_metadata_item (profile,
- CD_PROFILE_METADATA_SCREEN_BRIGHTNESS);
- if (gnome_rr_output_is_laptop (output) &&
- brightness_profile != NULL) {
- /* the percentage is stored in the profile metadata as
- * a string, not ideal, but it's all we have... */
- brightness_percentage = atoi (brightness_profile);
- gcm_session_set_output_percentage (brightness_percentage);
+ connection = g_bus_get_finish (res, &error);
+ if (connection == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not get session bus: %s", error->message);
+ g_error_free (error);
+ return;
}
- /* set the _ICC_PROFILE atom */
- ret = gcm_session_use_output_profile_for_screen (manager, output);
- if (ret) {
- ret = gcm_session_screen_set_icc_profile (manager,
- filename,
- &error);
- if (!ret) {
- g_warning ("failed to set screen _ICC_PROFILE: %s",
- error->message);
- g_clear_error (&error);
- }
- }
+ manager->connection = connection;
- /* create a vcgt for this icc file */
- ret = cd_profile_get_has_vcgt (profile);
- if (ret) {
- ret = gcm_session_device_set_gamma (output,
- profile,
- &error);
- if (!ret) {
- g_warning ("failed to set %s gamma tables: %s",
- cd_device_get_id (helper->device),
- error->message);
- g_error_free (error);
- goto out;
- }
- } else {
- ret = gcm_session_device_reset_gamma (output,
- &error);
- if (!ret) {
- g_warning ("failed to reset %s gamma tables: %s",
- cd_device_get_id (helper->device),
- error->message);
- g_error_free (error);
- goto out;
- }
+ g_dbus_connection_register_object (connection,
+ CSD_COLOR_DBUS_PATH,
+ manager->introspection_data->interfaces[0],
+ &interface_vtable,
+ manager,
+ NULL,
+ NULL);
+
+ manager->name_id = g_bus_own_name_on_connection (connection,
+ CSD_COLOR_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ name_lost_handler_cb,
+ manager,
+ NULL);
+
+ /* setup night light module */
+ if (!csd_night_light_start (manager->nlight, &error)) {
+ g_warning ("Could not start night light module: %s", error->message);
+ g_error_free (error);
}
-out:
- gcm_session_async_helper_free (helper);
}
static void
-gcm_session_device_assign_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+register_manager_dbus (CsdColorManager *manager)
{
- CdDeviceKind kind;
- CdProfile *profile = NULL;
- gboolean ret;
- gchar *autogen_filename = NULL;
- gchar *autogen_path = NULL;
- GcmEdid *edid = NULL;
- GnomeRROutput *output = NULL;
- GError *error = NULL;
- const gchar *xrandr_id;
- GcmSessionAsyncHelper *helper;
- CdDevice *device = CD_DEVICE (object);
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* remove from assign array */
- g_hash_table_remove (manager->priv->device_assign_hash,
- cd_device_get_object_path (device));
-
- /* get properties */
- ret = cd_device_connect_finish (device, res, &error);
- if (!ret) {
- g_debug ("failed to connect to device: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* check we care */
- kind = cd_device_get_kind (device);
- if (kind != CD_DEVICE_KIND_DISPLAY)
- goto out;
+ manager->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (manager->introspection_data != NULL);
+ manager->bus_cancellable = g_cancellable_new ();
- g_debug ("need to assign display device %s",
- cd_device_get_id (device));
-
- /* get the GnomeRROutput for the device id */
- xrandr_id = cd_device_get_id (device);
- output = gcm_session_get_x11_output_by_id (manager,
- xrandr_id,
- &error);
- if (output == NULL) {
- g_warning ("no %s device found: %s",
- cd_device_get_id (device),
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* create profile from device edid if it exists */
- edid = gcm_session_get_output_edid (manager, output, &error);
- if (edid == NULL) {
- g_warning ("unable to get EDID for %s: %s",
- cd_device_get_id (device),
- error->message);
- g_clear_error (&error);
-
- } else {
- autogen_filename = g_strdup_printf ("edid-%s.icc",
- gcm_edid_get_checksum (edid));
- autogen_path = g_build_filename (g_get_user_data_dir (),
- "icc", autogen_filename, NULL);
- if (g_file_test (autogen_path, G_FILE_TEST_EXISTS)) {
- g_debug ("auto-profile edid %s exists", autogen_path);
- } else {
- g_debug ("auto-profile edid does not exist, creating as %s",
- autogen_path);
- ret = gcm_apply_create_icc_profile_for_edid (manager,
- edid,
- autogen_path,
- &error);
- if (!ret) {
- g_warning ("failed to create profile from EDID data: %s",
- error->message);
- g_clear_error (&error);
- }
- }
- }
-
- /* get the default profile for the device */
- profile = cd_device_get_default_profile (device);
- if (profile == NULL) {
- g_debug ("%s has no default profile to set",
- cd_device_get_id (device));
-
- /* the default output? */
- if (gnome_rr_output_get_is_primary (output)) {
- gdk_property_delete (priv->gdk_window,
- gdk_atom_intern_static_string ("_ICC_PROFILE"));
- gdk_property_delete (priv->gdk_window,
- gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION"));
- }
-
- /* reset, as we want linear profiles for profiling */
- ret = gcm_session_device_reset_gamma (output,
- &error);
- if (!ret) {
- g_warning ("failed to reset %s gamma tables: %s",
- cd_device_get_id (device),
- error->message);
- g_error_free (error);
- goto out;
- }
- goto out;
- }
-
- /* get properties */
- helper = g_new0 (GcmSessionAsyncHelper, 1);
- helper->output_id = gnome_rr_output_get_id (output);
- helper->manager = g_object_ref (manager);
- helper->device = g_object_ref (device);
- cd_profile_connect (profile,
- NULL,
- gcm_session_device_assign_profile_connect_cb,
- helper);
-out:
- g_free (autogen_filename);
- g_free (autogen_path);
- if (edid != NULL)
- g_object_unref (edid);
- if (profile != NULL)
- g_object_unref (profile);
-}
-
-static void
-gcm_session_device_assign (CsdColorManager *manager, CdDevice *device)
-{
- const gchar *key;
- gpointer found;
-
- /* are we already assigning this device */
- key = cd_device_get_object_path (device);
- found = g_hash_table_lookup (manager->priv->device_assign_hash, key);
- if (found != NULL) {
- g_debug ("assign for %s already in progress", key);
- return;
- }
- g_hash_table_insert (manager->priv->device_assign_hash,
- g_strdup (key),
- GINT_TO_POINTER (TRUE));
- cd_device_connect (device,
- NULL,
- gcm_session_device_assign_connect_cb,
- manager);
-}
-
-static void
-gcm_session_device_added_assign_cb (CdClient *client,
- CdDevice *device,
- CsdColorManager *manager)
-{
- gcm_session_device_assign (manager, device);
-}
-
-static void
-gcm_session_device_changed_assign_cb (CdClient *client,
- CdDevice *device,
- CsdColorManager *manager)
-{
- g_debug ("%s changed", cd_device_get_object_path (device));
- gcm_session_device_assign (manager, device);
-}
-
-static void
-gcm_session_create_device_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- CdDevice *device;
- GError *error = NULL;
-
- device = cd_client_create_device_finish (CD_CLIENT (object),
- res,
- &error);
- if (device == NULL) {
- if (error->domain != CD_CLIENT_ERROR ||
- error->code != CD_CLIENT_ERROR_ALREADY_EXISTS) {
- g_warning ("failed to create device: %s",
- error->message);
- }
- g_error_free (error);
- return;
- }
- g_object_unref (device);
-}
-
-static void
-gcm_session_add_x11_output (CsdColorManager *manager, GnomeRROutput *output)
-{
- const gchar *model = NULL;
- const gchar *serial = NULL;
- const gchar *vendor = NULL;
- gboolean ret;
- gchar *device_id = NULL;
- GcmEdid *edid;
- GError *error = NULL;
- GHashTable *device_props = NULL;
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* try to get edid */
- edid = gcm_session_get_output_edid (manager, output, &error);
- if (edid == NULL) {
- g_warning ("failed to get edid: %s",
- error->message);
- g_clear_error (&error);
- }
-
- /* prefer DMI data for the internal output */
- ret = gnome_rr_output_is_laptop (output);
- if (ret) {
- model = gcm_dmi_get_name (priv->dmi);
- vendor = gcm_dmi_get_vendor (priv->dmi);
- }
-
- /* use EDID data if we have it */
- if (edid != NULL) {
- if (model == NULL)
- model = gcm_edid_get_monitor_name (edid);
- if (vendor == NULL)
- vendor = gcm_edid_get_vendor_name (edid);
- if (serial == NULL)
- serial = gcm_edid_get_serial_number (edid);
- }
-
- /* ensure mandatory fields are set */
- if (model == NULL)
- model = gnome_rr_output_get_name (output);
- if (vendor == NULL)
- vendor = "unknown";
- if (serial == NULL)
- serial = "unknown";
-
- device_id = gcm_session_get_output_id (manager, output);
- g_debug ("output %s added", device_id);
- device_props = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL, NULL);
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_KIND,
- (gpointer) cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY));
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_MODE,
- (gpointer) cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL));
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_COLORSPACE,
- (gpointer) cd_colorspace_to_string (CD_COLORSPACE_RGB));
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_VENDOR,
- (gpointer) vendor);
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_MODEL,
- (gpointer) model);
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_SERIAL,
- (gpointer) serial);
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_METADATA_XRANDR_NAME,
- (gpointer) gnome_rr_output_get_name (output));
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY,
- gnome_rr_output_get_is_primary (output) ?
- (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_PRIMARY :
- (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_SECONDARY);
-
- /* set this so we can call the device a 'Laptop Screen' in the
- * control center main panel */
- if (gnome_rr_output_is_laptop (output)) {
- g_hash_table_insert (device_props,
- (gpointer) CD_DEVICE_PROPERTY_EMBEDDED,
- NULL);
- }
-
- cd_client_create_device (priv->client,
- device_id,
- CD_OBJECT_SCOPE_TEMP,
- device_props,
- NULL,
- gcm_session_create_device_cb,
- manager);
- g_free (device_id);
- if (device_props != NULL)
- g_hash_table_unref (device_props);
- if (edid != NULL)
- g_object_unref (edid);
-}
-
-
-static void
-cinnamon_rr_screen_output_added_cb (GnomeRRScreen *screen,
- GnomeRROutput *output,
- CsdColorManager *manager)
-{
- gcm_session_add_x11_output (manager, output);
-}
-
-static void
-gcm_session_screen_removed_delete_device_cb (GObject *object, GAsyncResult *res, gpointer user_data)
-{
- gboolean ret;
- GError *error = NULL;
-
- /* deleted device */
- ret = cd_client_delete_device_finish (CD_CLIENT (object),
- res,
- &error);
- if (!ret) {
- g_warning ("failed to delete device: %s",
- error->message);
- g_error_free (error);
- }
-}
-
-static void
-gcm_session_screen_removed_find_device_cb (GObject *object, GAsyncResult *res, gpointer user_data)
-{
- GError *error = NULL;
- CdDevice *device;
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- device = cd_client_find_device_finish (manager->priv->client,
- res,
- &error);
- if (device == NULL) {
- g_warning ("failed to find device: %s",
- error->message);
- g_error_free (error);
- return;
- }
- g_debug ("output %s found, and will be removed",
- cd_device_get_object_path (device));
- cd_client_delete_device (manager->priv->client,
- device,
- NULL,
- gcm_session_screen_removed_delete_device_cb,
- manager);
- g_object_unref (device);
-}
-
-static void
-cinnamon_rr_screen_output_removed_cb (GnomeRRScreen *screen,
- GnomeRROutput *output,
- CsdColorManager *manager)
-{
- g_debug ("output %s removed",
- gnome_rr_output_get_name (output));
- g_hash_table_remove (manager->priv->edid_cache,
- gnome_rr_output_get_name (output));
- cd_client_find_device_by_property (manager->priv->client,
- CD_DEVICE_METADATA_XRANDR_NAME,
- gnome_rr_output_get_name (output),
- NULL,
- gcm_session_screen_removed_find_device_cb,
- manager);
-}
-
-static void
-gcm_session_get_devices_cb (GObject *object, GAsyncResult *res, gpointer user_data)
-{
- CdDevice *device;
- GError *error = NULL;
- GPtrArray *array;
- guint i;
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- array = cd_client_get_devices_finish (CD_CLIENT (object), res, &error);
- if (array == NULL) {
- g_warning ("failed to get devices: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
- for (i = 0; i < array->len; i++) {
- device = g_ptr_array_index (array, i);
- gcm_session_device_assign (manager, device);
- }
-out:
- if (array != NULL)
- g_ptr_array_unref (array);
-}
-
-static void
-gcm_session_profile_gamma_find_device_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- CdClient *client = CD_CLIENT (object);
- CdDevice *device = NULL;
- GError *error = NULL;
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- device = cd_client_find_device_by_property_finish (client,
- res,
- &error);
- if (device == NULL) {
- g_warning ("could not find device: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* get properties */
- cd_device_connect (device,
- NULL,
- gcm_session_device_assign_connect_cb,
- manager);
-out:
- if (device != NULL)
- g_object_unref (device);
-}
-
-/* We have to reset the gamma tables each time as if the primary output
- * has changed then different crtcs are going to be used.
- * See https://bugzilla.gnome.org/show_bug.cgi?id=660164 for an example */
-static void
-cinnamon_rr_screen_output_changed_cb (GnomeRRScreen *screen,
- CsdColorManager *manager)
-{
- GnomeRROutput **outputs;
- CsdColorManagerPrivate *priv = manager->priv;
- guint i;
-
- /* get X11 outputs */
- outputs = gnome_rr_screen_list_outputs (priv->x11_screen);
- if (outputs == NULL) {
- g_warning ("failed to get outputs");
- return;
- }
- for (i = 0; outputs[i] != NULL; i++) {
- if (!gnome_rr_output_is_connected (outputs[i]))
- continue;
-
- /* get CdDevice for this output */
- cd_client_find_device_by_property (manager->priv->client,
- CD_DEVICE_METADATA_XRANDR_NAME,
- gnome_rr_output_get_name (outputs[i]),
- NULL,
- gcm_session_profile_gamma_find_device_cb,
- manager);
- }
-
-}
-
-static void
-gcm_session_client_connect_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- gboolean ret;
- GError *error = NULL;
- GnomeRROutput **outputs;
- guint i;
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* connected */
- g_debug ("connected to colord");
- ret = cd_client_connect_finish (manager->priv->client, res, &error);
- if (!ret) {
- g_warning ("failed to connect to colord: %s", error->message);
- g_error_free (error);
- return;
- }
-
- /* is there an available colord instance? */
- ret = cd_client_get_has_server (manager->priv->client);
- if (!ret) {
- g_warning ("There is no colord server available");
- goto out;
- }
-
- /* add profiles */
- gcm_profile_store_search (priv->profile_store);
-
- /* add screens */
- gnome_rr_screen_refresh (priv->x11_screen, &error);
- if (error != NULL) {
- g_warning ("failed to refresh: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- /* get X11 outputs */
- outputs = gnome_rr_screen_list_outputs (priv->x11_screen);
- if (outputs == NULL) {
- g_warning ("failed to get outputs");
- goto out;
- }
- for (i = 0; outputs[i] != NULL; i++) {
- if (gnome_rr_output_is_connected (outputs[i]))
- gcm_session_add_x11_output (manager, outputs[i]);
- }
-
- /* only connect when colord is awake */
- g_signal_connect (priv->x11_screen, "output-connected",
- G_CALLBACK (cinnamon_rr_screen_output_added_cb),
- manager);
- g_signal_connect (priv->x11_screen, "output-disconnected",
- G_CALLBACK (cinnamon_rr_screen_output_removed_cb),
- manager);
- g_signal_connect (priv->x11_screen, "changed",
- G_CALLBACK (cinnamon_rr_screen_output_changed_cb),
- manager);
-
- g_signal_connect (priv->client, "profile-added",
- G_CALLBACK (gcm_session_profile_added_assign_cb),
- manager);
- g_signal_connect (priv->client, "device-added",
- G_CALLBACK (gcm_session_device_added_assign_cb),
- manager);
- g_signal_connect (priv->client, "device-changed",
- G_CALLBACK (gcm_session_device_changed_assign_cb),
- manager);
-
- /* set for each device that already exist */
- cd_client_get_devices (priv->client, NULL,
- gcm_session_get_devices_cb,
- manager);
-out:
- return;
-}
-
-gboolean
-csd_color_manager_start (CsdColorManager *manager,
- GError **error)
-{
- CsdColorManagerPrivate *priv = manager->priv;
- gboolean ret = FALSE;
-
- g_debug ("Starting color manager");
- cinnamon_settings_profile_start (NULL);
-
- /* coldplug the list of screens */
- priv->x11_screen = gnome_rr_screen_new (gdk_screen_get_default (), error);
- if (priv->x11_screen == NULL)
- goto out;
-
- cd_client_connect (priv->client,
- NULL,
- gcm_session_client_connect_cb,
- manager);
-
- /* success */
- ret = TRUE;
-out:
- cinnamon_settings_profile_end (NULL);
- return ret;
-}
-
-void
-csd_color_manager_stop (CsdColorManager *manager)
-{
- g_debug ("Stopping color manager");
-
- g_return_if_fail (manager->priv != NULL);
-
- if (manager->priv->settings != NULL) {
- g_object_unref (manager->priv->settings);
- manager->priv->settings = NULL;
- }
- if (manager->priv->client != NULL) {
- g_object_unref (manager->priv->client);
- manager->priv->client = NULL;
- }
- if (manager->priv->profile_store != NULL) {
- g_object_unref (manager->priv->profile_store);
- manager->priv->profile_store = NULL;
- }
- if (manager->priv->dmi != NULL) {
- g_object_unref (manager->priv->dmi);
- manager->priv->dmi = NULL;
- }
- if (manager->priv->session != NULL) {
- g_object_unref (manager->priv->session);
- manager->priv->session = NULL;
- }
- if (manager->priv->edid_cache != NULL) {
- g_hash_table_destroy (manager->priv->edid_cache);
- manager->priv->edid_cache = NULL;
- }
- if (manager->priv->device_assign_hash != NULL) {
- g_hash_table_destroy (manager->priv->device_assign_hash);
- manager->priv->device_assign_hash = NULL;
- }
- if (manager->priv->x11_screen != NULL) {
- g_object_unref (manager->priv->x11_screen);
- manager->priv->x11_screen = NULL;
- }
-}
-
-static void
-gcm_session_exec_control_center (CsdColorManager *manager)
-{
- gboolean ret;
- GError *error = NULL;
- GAppInfo *app_info;
- GdkAppLaunchContext *launch_context;
-
- /* setup the launch context so the startup notification is correct */
- launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ());
- app_info = g_app_info_create_from_commandline (BINDIR "/cinnamon-settings color",
- "cinnamon-settings",
- G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
- &error);
- if (app_info == NULL) {
- g_warning ("failed to create application info: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* launch cinnamon-settings */
- ret = g_app_info_launch (app_info,
- NULL,
- G_APP_LAUNCH_CONTEXT (launch_context),
- &error);
- if (!ret) {
- g_warning ("failed to launch cinnamon-settings: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-out:
- g_object_unref (launch_context);
- if (app_info != NULL)
- g_object_unref (app_info);
-}
-
-static void
-gcm_session_notify_cb (NotifyNotification *notification,
- gchar *action,
- gpointer user_data)
-{
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- if (g_strcmp0 (action, "recalibrate") == 0) {
- notify_notification_close (notification, NULL);
- gcm_session_exec_control_center (manager);
- }
-}
-
-static void
-closed_cb (NotifyNotification *notification, gpointer data)
-{
- g_object_unref (notification);
-}
-
-static gboolean
-gcm_session_notify_recalibrate (CsdColorManager *manager,
- const gchar *title,
- const gchar *message,
- CdDeviceKind kind)
-{
- gboolean ret;
- GError *error = NULL;
- NotifyNotification *notification;
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* show a bubble */
- notification = notify_notification_new (title, message, "preferences-color");
- notify_notification_set_timeout (notification, GCM_SESSION_NOTIFY_TIMEOUT);
- notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
- notify_notification_set_app_name (notification, _("Color"));
-
- /* TRANSLATORS: button: this is to open GCM */
- notify_notification_add_action (notification,
- "recalibrate",
- _("Recalibrate now"),
- gcm_session_notify_cb,
- priv, NULL);
-
- g_signal_connect (notification, "closed", G_CALLBACK (closed_cb), NULL);
- ret = notify_notification_show (notification, &error);
- if (!ret) {
- g_warning ("failed to show notification: %s",
- error->message);
- g_error_free (error);
- }
- return ret;
-}
-
-static gchar *
-gcm_session_device_get_title (CdDevice *device)
-{
- const gchar *vendor;
- const gchar *model;
-
- model = cd_device_get_model (device);
- vendor = cd_device_get_vendor (device);
- if (model != NULL && vendor != NULL)
- return g_strdup_printf ("%s - %s", vendor, model);
- if (vendor != NULL)
- return g_strdup (vendor);
- if (model != NULL)
- return g_strdup (model);
- return g_strdup (cd_device_get_id (device));
-}
-
-static void
-gcm_session_notify_device (CsdColorManager *manager, CdDevice *device)
-{
- CdDeviceKind kind;
- const gchar *title;
- gchar *device_title = NULL;
- gchar *message;
- guint threshold;
- glong since;
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* TRANSLATORS: this is when the device has not been recalibrated in a while */
- title = _("Recalibration required");
- device_title = gcm_session_device_get_title (device);
-
- /* check we care */
- kind = cd_device_get_kind (device);
- if (kind == CD_DEVICE_KIND_DISPLAY) {
-
- /* get from GSettings */
- threshold = g_settings_get_uint (priv->settings,
- GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD);
-
- /* TRANSLATORS: this is when the display has not been recalibrated in a while */
- message = g_strdup_printf (_("The display '%s' should be recalibrated soon."),
- device_title);
- } else {
-
- /* get from GSettings */
- threshold = g_settings_get_uint (priv->settings,
- GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD);
-
- /* TRANSLATORS: this is when the printer has not been recalibrated in a while */
- message = g_strdup_printf (_("The printer '%s' should be recalibrated soon."),
- device_title);
- }
-
- /* check if we need to notify */
- since = (g_get_real_time () - cd_device_get_modified (device)) / G_USEC_PER_SEC;
- if (threshold > since)
- gcm_session_notify_recalibrate (manager, title, message, kind);
- g_free (device_title);
- g_free (message);
-}
-
-static void
-gcm_session_profile_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- const gchar *filename;
- gboolean ret;
- gchar *basename = NULL;
- const gchar *data_source;
- GError *error = NULL;
- CdProfile *profile = CD_PROFILE (object);
- GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
- CsdColorManager *manager = CSD_COLOR_MANAGER (helper->manager);
-
- ret = cd_profile_connect_finish (profile,
- res,
- &error);
- if (!ret) {
- g_warning ("failed to connect to profile: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* ensure it's a profile generated by us */
- data_source = cd_profile_get_metadata_item (profile,
- CD_PROFILE_METADATA_DATA_SOURCE);
- if (data_source == NULL) {
-
- /* existing profiles from gnome-color-manager < 3.1
- * won't have the extra metadata values added */
- filename = cd_profile_get_filename (profile);
- if (filename == NULL)
- goto out;
- basename = g_path_get_basename (filename);
- if (!g_str_has_prefix (basename, "GCM")) {
- g_debug ("not a GCM profile for %s: %s",
- cd_device_get_id (helper->device), filename);
- goto out;
- }
-
- /* ensure it's been created from a calibration, rather than from
- * auto-EDID */
- } else if (g_strcmp0 (data_source,
- CD_PROFILE_METADATA_DATA_SOURCE_CALIB) != 0) {
- g_debug ("not a calib profile for %s",
- cd_device_get_id (helper->device));
- goto out;
- }
-
- /* handle device */
- gcm_session_notify_device (manager, helper->device);
-out:
- gcm_session_async_helper_free (helper);
- g_free (basename);
-}
-
-static void
-gcm_session_device_connect_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- gboolean ret;
- GError *error = NULL;
- CdDeviceKind kind;
- CdProfile *profile = NULL;
- CdDevice *device = CD_DEVICE (object);
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
- GcmSessionAsyncHelper *helper;
-
- ret = cd_device_connect_finish (device,
- res,
- &error);
- if (!ret) {
- g_warning ("failed to connect to device: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- /* check we care */
- kind = cd_device_get_kind (device);
- if (kind != CD_DEVICE_KIND_DISPLAY &&
- kind != CD_DEVICE_KIND_PRINTER)
- goto out;
-
- /* ensure we have a profile */
- profile = cd_device_get_default_profile (device);
- if (profile == NULL) {
- g_debug ("no profile set for %s", cd_device_get_id (device));
- goto out;
- }
-
- /* connect to the profile */
- helper = g_new0 (GcmSessionAsyncHelper, 1);
- helper->manager = g_object_ref (manager);
- helper->device = g_object_ref (device);
- cd_profile_connect (profile,
- NULL,
- gcm_session_profile_connect_cb,
- helper);
-out:
- if (profile != NULL)
- g_object_unref (profile);
-}
-
-static void
-gcm_session_device_added_notify_cb (CdClient *client,
- CdDevice *device,
- CsdColorManager *manager)
-{
- /* connect to the device to get properties */
- cd_device_connect (device,
- NULL,
- gcm_session_device_connect_cb,
- manager);
-}
-
-static gchar *
-gcm_session_get_precooked_md5 (cmsHPROFILE lcms_profile)
-{
- cmsUInt8Number profile_id[16];
- gboolean md5_precooked = FALSE;
- guint i;
- gchar *md5 = NULL;
-
- /* check to see if we have a pre-cooked MD5 */
- cmsGetHeaderProfileID (lcms_profile, profile_id);
- for (i = 0; i < 16; i++) {
- if (profile_id[i] != 0) {
- md5_precooked = TRUE;
- break;
- }
- }
- if (!md5_precooked)
- goto out;
-
- /* convert to a hex string */
- md5 = g_new0 (gchar, 32 + 1);
- for (i = 0; i < 16; i++)
- g_snprintf (md5 + i*2, 3, "%02x", profile_id[i]);
-out:
- return md5;
-}
-
-static gchar *
-gcm_session_get_md5_for_filename (const gchar *filename,
- GError **error)
-{
- gboolean ret;
- gchar *checksum = NULL;
- gchar *data = NULL;
- gsize length;
- cmsHPROFILE lcms_profile = NULL;
-
- /* get the internal profile id, if it exists */
- lcms_profile = cmsOpenProfileFromFile (filename, "r");
- if (lcms_profile == NULL) {
- g_set_error_literal (error,
- CSD_COLOR_MANAGER_ERROR,
- CSD_COLOR_MANAGER_ERROR_FAILED,
- "failed to load: not an ICC profile");
- goto out;
- }
- checksum = gcm_session_get_precooked_md5 (lcms_profile);
- if (checksum != NULL)
- goto out;
-
- /* generate checksum */
- ret = g_file_get_contents (filename, &data, &length, error);
- if (!ret)
- goto out;
- checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
- (const guchar *) data,
- length);
-out:
- g_free (data);
- if (lcms_profile != NULL)
- cmsCloseProfile (lcms_profile);
- return checksum;
-}
-
-static void
-gcm_session_create_profile_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- CdProfile *profile;
- GError *error = NULL;
- CdClient *client = CD_CLIENT (object);
-
- profile = cd_client_create_profile_finish (client, res, &error);
- if (profile == NULL) {
- if (error->domain != CD_CLIENT_ERROR ||
- error->code != CD_CLIENT_ERROR_ALREADY_EXISTS)
- g_warning ("%s", error->message);
- g_error_free (error);
- return;
- }
- g_object_unref (profile);
-}
-
-static void
-gcm_session_profile_store_added_cb (GcmProfileStore *profile_store,
- const gchar *filename,
- CsdColorManager *manager)
-{
- gchar *checksum = NULL;
- gchar *profile_id = NULL;
- GError *error = NULL;
- GHashTable *profile_props = NULL;
- CsdColorManagerPrivate *priv = manager->priv;
-
- g_debug ("profile %s added", filename);
-
- /* generate ID */
- checksum = gcm_session_get_md5_for_filename (filename, &error);
- if (checksum == NULL) {
- g_debug ("failed to get profile checksum for %s: %s",
- filename, error->message);
- g_error_free (error);
- goto out;
- }
- profile_id = g_strdup_printf ("icc-%s", checksum);
- profile_props = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL, NULL);
- g_hash_table_insert (profile_props,
- (gpointer) CD_PROFILE_PROPERTY_FILENAME,
- (gpointer) filename);
- g_hash_table_insert (profile_props,
- (gpointer) CD_PROFILE_METADATA_FILE_CHECKSUM,
- (gpointer) checksum);
- cd_client_create_profile (priv->client,
- profile_id,
- CD_OBJECT_SCOPE_TEMP,
- profile_props,
- NULL,
- gcm_session_create_profile_cb,
- manager);
-out:
- g_free (checksum);
- g_free (profile_id);
- if (profile_props != NULL)
- g_hash_table_unref (profile_props);
-}
-
-static void
-gcm_session_delete_profile_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- gboolean ret;
- GError *error = NULL;
- CdClient *client = CD_CLIENT (object);
-
- ret = cd_client_delete_profile_finish (client, res, &error);
- if (!ret) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
-}
-
-static void
-gcm_session_find_profile_by_filename_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GError *error = NULL;
- CdProfile *profile;
- CdClient *client = CD_CLIENT (object);
- CsdColorManager *manager = CSD_COLOR_MANAGER (user_data);
-
- profile = cd_client_find_profile_by_filename_finish (client, res, &error);
- if (profile == NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- goto out;
- }
-
- /* remove it from colord */
- cd_client_delete_profile (manager->priv->client,
- profile,
- NULL,
- gcm_session_delete_profile_cb,
- manager);
-out:
- if (profile != NULL)
- g_object_unref (profile);
-}
-
-static void
-gcm_session_profile_store_removed_cb (GcmProfileStore *profile_store,
- const gchar *filename,
- CsdColorManager *manager)
-{
- /* find the ID for the filename */
- g_debug ("filename %s removed", filename);
- cd_client_find_profile_by_filename (manager->priv->client,
- filename,
- NULL,
- gcm_session_find_profile_by_filename_cb,
- manager);
-}
-
-static void
-gcm_session_sensor_added_cb (CdClient *client,
- CdSensor *sensor,
- CsdColorManager *manager)
-{
- ca_context_play (ca_gtk_context_get (), 0,
- CA_PROP_EVENT_ID, "device-added",
- /* TRANSLATORS: this is the application name */
- CA_PROP_APPLICATION_NAME, _("Cinnamon Settings Daemon Color Plugin"),
- /* TRANSLATORS: this is a sound description */
- CA_PROP_EVENT_DESCRIPTION, _("Color calibration device added"), NULL);
-
- /* open up the color prefs window */
- gcm_session_exec_control_center (manager);
-}
-
-static void
-gcm_session_sensor_removed_cb (CdClient *client,
- CdSensor *sensor,
- CsdColorManager *manager)
-{
- ca_context_play (ca_gtk_context_get (), 0,
- CA_PROP_EVENT_ID, "device-removed",
- /* TRANSLATORS: this is the application name */
- CA_PROP_APPLICATION_NAME, _("Cinnamon Settings Daemon Color Plugin"),
- /* TRANSLATORS: this is a sound description */
- CA_PROP_EVENT_DESCRIPTION, _("Color calibration device removed"), NULL);
-}
-
-static void
-gcm_session_active_changed_cb (CinnamonSettingsSession *session,
- GParamSpec *pspec,
- CsdColorManager *manager)
-{
- CinnamonSettingsSessionState state_new;
- CsdColorManagerPrivate *priv = manager->priv;
-
- /* not yet connected to the daemon */
- if (!cd_client_get_connected (priv->client))
- return;
-
- /* When doing the fast-user-switch into a new account, load the
- * new users chosen profiles.
- *
- * If this is the first time the CinnamonSettingsSession has been
- * loaded, then we'll get a change from unknown to active
- * and we want to avoid reprobing the devices for that.
- */
- state_new = cinnamon_settings_session_get_state (session);
- if (priv->session_state != CINNAMON_SETTINGS_SESSION_STATE_UNKNOWN &&
- state_new == CINNAMON_SETTINGS_SESSION_STATE_ACTIVE) {
- g_debug ("Done switch to new account, reload devices");
- cd_client_get_devices (manager->priv->client, NULL,
- gcm_session_get_devices_cb,
- manager);
- }
- priv->session_state = state_new;
-}
-
-static void
-csd_color_manager_class_init (CsdColorManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = csd_color_manager_finalize;
-
- g_type_class_add_private (klass, sizeof (CsdColorManagerPrivate));
-}
-
-static void
-csd_color_manager_init (CsdColorManager *manager)
-{
- CsdColorManagerPrivate *priv;
- priv = manager->priv = CSD_COLOR_MANAGER_GET_PRIVATE (manager);
-
- /* track the active session */
- priv->session = cinnamon_settings_session_new ();
- priv->session_state = cinnamon_settings_session_get_state (priv->session);
- g_signal_connect (priv->session, "notify::state",
- G_CALLBACK (gcm_session_active_changed_cb),
- manager);
-
- /* set the _ICC_PROFILE atoms on the root screen */
- priv->gdk_window = gdk_screen_get_root_window (gdk_screen_get_default ());
-
- /* parsing the EDID is expensive */
- priv->edid_cache = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- /* we don't want to assign devices multiple times at startup */
- priv->device_assign_hash = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
-
- /* use DMI data for internal panels */
- priv->dmi = gcm_dmi_new ();
-
- priv->settings = g_settings_new ("org.cinnamon.settings-daemon.plugins.color");
- priv->client = cd_client_new ();
- g_signal_connect (priv->client, "device-added",
- G_CALLBACK (gcm_session_device_added_notify_cb),
- manager);
- g_signal_connect (priv->client, "sensor-added",
- G_CALLBACK (gcm_session_sensor_added_cb),
- manager);
- g_signal_connect (priv->client, "sensor-removed",
- G_CALLBACK (gcm_session_sensor_removed_cb),
- manager);
-
- /* have access to all user profiles */
- priv->profile_store = gcm_profile_store_new ();
- g_signal_connect (priv->profile_store, "added",
- G_CALLBACK (gcm_session_profile_store_added_cb),
- manager);
- g_signal_connect (priv->profile_store, "removed",
- G_CALLBACK (gcm_session_profile_store_removed_cb),
- manager);
-}
-
-static void
-csd_color_manager_finalize (GObject *object)
-{
- g_return_if_fail (object != NULL);
- G_OBJECT_CLASS (csd_color_manager_parent_class)->finalize (object);
+ g_bus_get (G_BUS_TYPE_SESSION,
+ manager->bus_cancellable,
+ (GAsyncReadyCallback) on_bus_gotten,
+ manager);
}
CsdColorManager *
@@ -2357,6 +495,7 @@ csd_color_manager_new (void)
manager_object = g_object_new (CSD_TYPE_COLOR_MANAGER, NULL);
g_object_add_weak_pointer (manager_object,
(gpointer *) &manager_object);
+ register_manager_dbus (manager_object);
}
return CSD_COLOR_MANAGER (manager_object);
diff --git a/plugins/color/csd-color-manager.h b/plugins/color/csd-color-manager.h
index d0f899ae..83ca0dd3 100644
--- a/plugins/color/csd-color-manager.h
+++ b/plugins/color/csd-color-manager.h
@@ -14,8 +14,7 @@
* 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 - Suite 500, Boston, MA 02110-1335, USA.
+ * along with this program; if not, see .
*
*/
@@ -27,32 +26,14 @@
G_BEGIN_DECLS
#define CSD_TYPE_COLOR_MANAGER (csd_color_manager_get_type ())
-#define CSD_COLOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CSD_TYPE_COLOR_MANAGER, CsdColorManager))
-#define CSD_COLOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CSD_TYPE_COLOR_MANAGER, CsdColorManagerClass))
-#define CSD_IS_COLOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CSD_TYPE_COLOR_MANAGER))
-#define CSD_IS_COLOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CSD_TYPE_COLOR_MANAGER))
-#define CSD_COLOR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CSD_TYPE_COLOR_MANAGER, CsdColorManagerClass))
#define CSD_COLOR_MANAGER_ERROR (csd_color_manager_error_quark ())
-
-typedef struct CsdColorManagerPrivate CsdColorManagerPrivate;
-
-typedef struct
-{
- GObject parent;
- CsdColorManagerPrivate *priv;
-} CsdColorManager;
-
-typedef struct
-{
- GObjectClass parent_class;
-} CsdColorManagerClass;
+G_DECLARE_FINAL_TYPE (CsdColorManager, csd_color_manager, CSD, COLOR_MANAGER, GObject)
enum
{
CSD_COLOR_MANAGER_ERROR_FAILED
};
-GType csd_color_manager_get_type (void);
GQuark csd_color_manager_error_quark (void);
CsdColorManager * csd_color_manager_new (void);
diff --git a/plugins/color/csd-color-profiles.c b/plugins/color/csd-color-profiles.c
new file mode 100644
index 00000000..e340c406
--- /dev/null
+++ b/plugins/color/csd-color-profiles.c
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "csd-color-profiles.h"
+
+struct _CsdColorProfiles
+{
+ GObject parent;
+
+ GCancellable *cancellable;
+ CdClient *client;
+ CdIccStore *icc_store;
+};
+
+static void csd_color_profiles_class_init (CsdColorProfilesClass *klass);
+static void csd_color_profiles_init (CsdColorProfiles *color_profiles);
+static void csd_color_profiles_finalize (GObject *object);
+
+G_DEFINE_TYPE (CsdColorProfiles, csd_color_profiles, G_TYPE_OBJECT)
+
+static void
+csd_color_profiles_class_init (CsdColorProfilesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = csd_color_profiles_finalize;
+}
+
+static void
+ccm_session_client_connect_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+ CdClient *client = CD_CLIENT (source_object);
+ CsdColorProfiles *profiles;
+
+ /* connected */
+ ret = cd_client_connect_finish (client, res, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to connect to colord: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* is there an available colord instance? */
+ profiles = CSD_COLOR_PROFILES (user_data);
+ ret = cd_client_get_has_server (profiles->client);
+ if (!ret) {
+ g_warning ("There is no colord server available");
+ return;
+ }
+
+ /* add profiles */
+ ret = cd_icc_store_search_kind (profiles->icc_store,
+ CD_ICC_STORE_SEARCH_KIND_USER,
+ CD_ICC_STORE_SEARCH_FLAGS_CREATE_LOCATION,
+ profiles->cancellable,
+ &error);
+ if (!ret) {
+ g_warning ("failed to add user icc: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+gboolean
+csd_color_profiles_start (CsdColorProfiles *profiles,
+ GError **error)
+{
+ /* use a fresh cancellable for each start->stop operation */
+ g_cancellable_cancel (profiles->cancellable);
+ g_clear_object (&profiles->cancellable);
+ profiles->cancellable = g_cancellable_new ();
+
+ cd_client_connect (profiles->client,
+ profiles->cancellable,
+ ccm_session_client_connect_cb,
+ profiles);
+
+ return TRUE;
+}
+
+void
+csd_color_profiles_stop (CsdColorProfiles *profiles)
+{
+ g_cancellable_cancel (profiles->cancellable);
+}
+
+static void
+ccm_session_create_profile_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdProfile *profile;
+ GError *error = NULL;
+ CdClient *client = CD_CLIENT (object);
+
+ profile = cd_client_create_profile_finish (client, res, &error);
+ if (profile == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ !g_error_matches (error, CD_CLIENT_ERROR, CD_CLIENT_ERROR_ALREADY_EXISTS))
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+ g_object_unref (profile);
+}
+
+static void
+ccm_session_icc_store_added_cb (CdIccStore *icc_store,
+ CdIcc *icc,
+ CsdColorProfiles *profiles)
+{
+ cd_client_create_profile_for_icc (profiles->client,
+ icc,
+ CD_OBJECT_SCOPE_TEMP,
+ profiles->cancellable,
+ ccm_session_create_profile_cb,
+ profiles);
+}
+
+static void
+ccm_session_delete_profile_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+ CdClient *client = CD_CLIENT (object);
+
+ ret = cd_client_delete_profile_finish (client, res, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+ccm_session_find_profile_by_filename_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ CdProfile *profile;
+ CdClient *client = CD_CLIENT (object);
+ CsdColorProfiles *profiles = CSD_COLOR_PROFILES (user_data);
+
+ profile = cd_client_find_profile_by_filename_finish (client, res, &error);
+ if (profile == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* remove it from colord */
+ cd_client_delete_profile (profiles->client,
+ profile,
+ profiles->cancellable,
+ ccm_session_delete_profile_cb,
+ profiles);
+out:
+ if (profile != NULL)
+ g_object_unref (profile);
+}
+
+static void
+ccm_session_icc_store_removed_cb (CdIccStore *icc_store,
+ CdIcc *icc,
+ CsdColorProfiles *profiles)
+{
+ /* find the ID for the filename */
+ g_debug ("filename %s removed", cd_icc_get_filename (icc));
+ cd_client_find_profile_by_filename (profiles->client,
+ cd_icc_get_filename (icc),
+ profiles->cancellable,
+ ccm_session_find_profile_by_filename_cb,
+ profiles);
+}
+
+static void
+csd_color_profiles_init (CsdColorProfiles *profiles)
+{
+ /* have access to all user profiles */
+ profiles->client = cd_client_new ();
+ profiles->icc_store = cd_icc_store_new ();
+ cd_icc_store_set_load_flags (profiles->icc_store,
+ CD_ICC_LOAD_FLAGS_FALLBACK_MD5);
+ g_signal_connect (profiles->icc_store, "added",
+ G_CALLBACK (ccm_session_icc_store_added_cb),
+ profiles);
+ g_signal_connect (profiles->icc_store, "removed",
+ G_CALLBACK (ccm_session_icc_store_removed_cb),
+ profiles);
+}
+
+static void
+csd_color_profiles_finalize (GObject *object)
+{
+ CsdColorProfiles *profiles;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CSD_IS_COLOR_PROFILES (object));
+
+ profiles = CSD_COLOR_PROFILES (object);
+
+ g_cancellable_cancel (profiles->cancellable);
+ g_clear_object (&profiles->cancellable);
+ g_clear_object (&profiles->icc_store);
+ g_clear_object (&profiles->client);
+
+ G_OBJECT_CLASS (csd_color_profiles_parent_class)->finalize (object);
+}
+
+CsdColorProfiles *
+csd_color_profiles_new (void)
+{
+ CsdColorProfiles *profiles;
+ profiles = g_object_new (CSD_TYPE_COLOR_PROFILES, NULL);
+ return CSD_COLOR_PROFILES (profiles);
+}
diff --git a/plugins/color/csd-color-profiles.h b/plugins/color/csd-color-profiles.h
new file mode 100644
index 00000000..87ffafcc
--- /dev/null
+++ b/plugins/color/csd-color-profiles.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#ifndef __CSD_COLOR_PROFILES_H
+#define __CSD_COLOR_PROFILES_H
+
+#include
+
+G_BEGIN_DECLS
+
+#define CSD_TYPE_COLOR_PROFILES (csd_color_profiles_get_type ())
+G_DECLARE_FINAL_TYPE (CsdColorProfiles, csd_color_profiles, CSD, COLOR_PROFILES, GObject)
+
+GQuark csd_color_profiles_error_quark (void);
+
+CsdColorProfiles * csd_color_profiles_new (void);
+gboolean csd_color_profiles_start (CsdColorProfiles *profiles,
+ GError **error);
+void csd_color_profiles_stop (CsdColorProfiles *profiles);
+
+G_END_DECLS
+
+#endif /* __CSD_COLOR_PROFILES_H */
diff --git a/plugins/color/csd-color-state.c b/plugins/color/csd-color-state.c
new file mode 100644
index 00000000..d32105ca
--- /dev/null
+++ b/plugins/color/csd-color-state.c
@@ -0,0 +1,1597 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include
+
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "cinnamon-session-dbus.h"
+
+#include "csd-color-manager.h"
+#include "csd-color-state.h"
+#include "ccm-edid.h"
+
+#define CSD_DBUS_NAME "org.gnome.SettingsDaemon"
+#define CSD_DBUS_PATH "/org/gnome/SettingsDaemon"
+#define CSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon"
+
+static void ccm_session_set_gamma_for_all_devices (CsdColorState *state);
+
+struct _CsdColorState
+{
+ GObject parent;
+
+ GCancellable *cancellable;
+ GDBusProxy *session;
+ CdClient *client;
+ GnomeRRScreen *state_screen;
+ GHashTable *edid_cache;
+ GdkWindow *gdk_window;
+ gboolean session_is_active;
+ GHashTable *device_assign_hash;
+ guint color_temperature;
+};
+
+static void csd_color_state_class_init (CsdColorStateClass *klass);
+static void csd_color_state_init (CsdColorState *color_state);
+static void csd_color_state_finalize (GObject *object);
+
+G_DEFINE_TYPE (CsdColorState, csd_color_state, G_TYPE_OBJECT)
+
+/* see http://www.oyranos.org/wiki/index.php?title=ICC_Profiles_in_X_Specification_0.3 */
+#define CCM_ICC_PROFILE_IN_X_VERSION_MAJOR 0
+#define CCM_ICC_PROFILE_IN_X_VERSION_MINOR 3
+
+typedef struct {
+ guint32 red;
+ guint32 green;
+ guint32 blue;
+} GnomeRROutputClutItem;
+
+GQuark
+csd_color_state_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("csd_color_state_error");
+ return quark;
+}
+
+static CcmEdid *
+ccm_session_get_output_edid (CsdColorState *state, GnomeRROutput *output, GError **error)
+{
+ const guint8 *data;
+ gsize size;
+ CcmEdid *edid = NULL;
+ gboolean ret;
+
+ /* can we find it in the cache */
+ edid = g_hash_table_lookup (state->edid_cache,
+ gnome_rr_output_get_name (output));
+ if (edid != NULL) {
+ g_object_ref (edid);
+ return edid;
+ }
+
+ /* parse edid */
+ data = gnome_rr_output_get_edid_data (output, &size);
+ if (data == NULL || size == 0) {
+ g_set_error_literal (error,
+ GNOME_RR_ERROR,
+ GNOME_RR_ERROR_UNKNOWN,
+ "unable to get EDID for output");
+ return NULL;
+ }
+ edid = ccm_edid_new ();
+ ret = ccm_edid_parse (edid, data, size, error);
+ if (!ret) {
+ g_object_unref (edid);
+ return NULL;
+ }
+
+ /* add to cache */
+ g_hash_table_insert (state->edid_cache,
+ g_strdup (gnome_rr_output_get_name (output)),
+ g_object_ref (edid));
+
+ return edid;
+}
+
+static gboolean
+ccm_session_screen_set_icc_profile (CsdColorState *state,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *data = NULL;
+ gsize length;
+ guint version_data;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ /* wayland */
+ if (state->gdk_window == NULL) {
+ g_debug ("not setting atom as running under wayland");
+ return TRUE;
+ }
+
+ g_debug ("setting root window ICC profile atom from %s", filename);
+
+ /* get contents of file */
+ if (!g_file_get_contents (filename, &data, &length, error))
+ return FALSE;
+
+ /* set profile property */
+ gdk_property_change (state->gdk_window,
+ gdk_atom_intern_static_string ("_ICC_PROFILE"),
+ gdk_atom_intern_static_string ("CARDINAL"),
+ 8,
+ GDK_PROP_MODE_REPLACE,
+ (const guchar *) data, length);
+
+ /* set version property */
+ version_data = CCM_ICC_PROFILE_IN_X_VERSION_MAJOR * 100 +
+ CCM_ICC_PROFILE_IN_X_VERSION_MINOR * 1;
+ gdk_property_change (state->gdk_window,
+ gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION"),
+ gdk_atom_intern_static_string ("CARDINAL"),
+ 8,
+ GDK_PROP_MODE_REPLACE,
+ (const guchar *) &version_data, 1);
+
+ g_free (data);
+ return TRUE;
+}
+
+void
+csd_color_state_set_temperature (CsdColorState *state, guint temperature)
+{
+ g_return_if_fail (CSD_IS_COLOR_STATE (state));
+
+ if (state->color_temperature == temperature)
+ return;
+
+ state->color_temperature = temperature;
+ ccm_session_set_gamma_for_all_devices (state);
+}
+
+guint
+csd_color_state_get_temperature (CsdColorState *state)
+{
+ g_return_val_if_fail (CSD_IS_COLOR_STATE (state), 0);
+ return state->color_temperature;
+}
+
+static gchar *
+ccm_session_get_output_id (CsdColorState *state, GnomeRROutput *output)
+{
+ const gchar *name;
+ const gchar *serial;
+ const gchar *vendor;
+ CcmEdid *edid = NULL;
+ GString *device_id;
+ GError *error = NULL;
+
+ /* all output devices are prefixed with this */
+ device_id = g_string_new ("xrandr");
+
+ /* get the output EDID if possible */
+ edid = ccm_session_get_output_edid (state, output, &error);
+ if (edid == NULL) {
+ g_debug ("no edid for %s [%s], falling back to connection name",
+ gnome_rr_output_get_name (output),
+ error->message);
+ g_error_free (error);
+ g_string_append_printf (device_id,
+ "-%s",
+ gnome_rr_output_get_name (output));
+ goto out;
+ }
+
+ /* check EDID data is okay to use */
+ vendor = ccm_edid_get_vendor_name (edid);
+ name = ccm_edid_get_monitor_name (edid);
+ serial = ccm_edid_get_serial_number (edid);
+ if (vendor == NULL && name == NULL && serial == NULL) {
+ g_debug ("edid invalid for %s, falling back to connection name",
+ gnome_rr_output_get_name (output));
+ g_string_append_printf (device_id,
+ "-%s",
+ gnome_rr_output_get_name (output));
+ goto out;
+ }
+
+ /* use EDID data */
+ if (vendor != NULL)
+ g_string_append_printf (device_id, "-%s", vendor);
+ if (name != NULL)
+ g_string_append_printf (device_id, "-%s", name);
+ if (serial != NULL)
+ g_string_append_printf (device_id, "-%s", serial);
+out:
+ if (edid != NULL)
+ g_object_unref (edid);
+ return g_string_free (device_id, FALSE);
+}
+
+typedef struct {
+ CsdColorState *state;
+ CdProfile *profile;
+ CdDevice *device;
+ guint32 output_id;
+} CcmSessionAsyncHelper;
+
+static void
+ccm_session_async_helper_free (CcmSessionAsyncHelper *helper)
+{
+ if (helper->state != NULL)
+ g_object_unref (helper->state);
+ if (helper->profile != NULL)
+ g_object_unref (helper->profile);
+ if (helper->device != NULL)
+ g_object_unref (helper->device);
+ g_free (helper);
+}
+
+static gboolean
+ccm_utils_mkdir_for_filename (GFile *file, GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *parent_dir = NULL;
+
+ /* get parent directory */
+ parent_dir = g_file_get_parent (file);
+ if (parent_dir == NULL) {
+ g_set_error_literal (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "could not get parent dir");
+ goto out;
+ }
+
+ /* ensure desination does not already exist */
+ ret = g_file_query_exists (parent_dir, NULL);
+ if (ret)
+ goto out;
+ ret = g_file_make_directory_with_parents (parent_dir, NULL, error);
+ if (!ret)
+ goto out;
+out:
+ if (parent_dir != NULL)
+ g_object_unref (parent_dir);
+ return ret;
+}
+
+static gboolean
+ccm_get_system_icc_profile (CsdColorState *state,
+ GFile *file)
+{
+ const char efi_path[] = "/sys/firmware/efi/efivars/INTERNAL_PANEL_COLOR_INFO-01e1ada1-79f2-46b3-8d3e-71fc0996ca6b";
+ /* efi variables have a 4-byte header */
+ const int efi_var_header_length = 4;
+ g_autoptr(GFile) efi_file = g_file_new_for_path (efi_path);
+ gboolean ret;
+ g_autofree char *data = NULL;
+ gsize length;
+ g_autoptr(GError) error = NULL;
+
+ ret = g_file_query_exists (efi_file, NULL);
+ if (!ret)
+ return FALSE;
+
+ ret = g_file_load_contents (efi_file,
+ NULL /* cancellable */,
+ &data,
+ &length,
+ NULL /* etag_out */,
+ &error);
+
+ if (!ret) {
+ g_warning ("failed to read EFI system color profile: %s",
+ error->message);
+ return FALSE;
+ }
+
+ if (length <= efi_var_header_length) {
+ g_warning ("EFI system color profile was too short");
+ return FALSE;
+ }
+
+ ret = g_file_replace_contents (file,
+ data + efi_var_header_length,
+ length - efi_var_header_length,
+ NULL /* etag */,
+ FALSE /* make_backup */,
+ G_FILE_CREATE_NONE,
+ NULL /* new_etag */,
+ NULL /* cancellable */,
+ &error);
+ if (!ret) {
+ g_warning ("failed to write system color profile: %s",
+ error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ccm_apply_create_icc_profile_for_edid (CsdColorState *state,
+ CdDevice *device,
+ CcmEdid *edid,
+ GFile *file,
+ GError **error)
+{
+ CdIcc *icc = NULL;
+ const gchar *data;
+ gboolean ret = FALSE;
+
+ /* ensure the per-user directory exists */
+ ret = ccm_utils_mkdir_for_filename (file, error);
+ if (!ret)
+ goto out;
+
+ /* create our generated profile */
+ icc = cd_icc_new ();
+ ret = cd_icc_create_from_edid (icc,
+ ccm_edid_get_gamma (edid),
+ ccm_edid_get_red (edid),
+ ccm_edid_get_green (edid),
+ ccm_edid_get_blue (edid),
+ ccm_edid_get_white (edid),
+ error);
+ if (!ret)
+ goto out;
+
+ /* set copyright */
+ cd_icc_set_copyright (icc, NULL,
+ /* deliberately not translated */
+ "This profile is free of known copyright restrictions.");
+
+ /* set model and title */
+ data = ccm_edid_get_monitor_name (edid);
+ if (data == NULL)
+ data = cd_client_get_system_model (state->client);
+ if (data == NULL)
+ data = "Unknown monitor";
+ cd_icc_set_model (icc, NULL, data);
+ cd_icc_set_description (icc, NULL, data);
+
+ /* get manufacturer */
+ data = ccm_edid_get_vendor_name (edid);
+ if (data == NULL)
+ data = cd_client_get_system_vendor (state->client);
+ if (data == NULL)
+ data = "Unknown vendor";
+ cd_icc_set_manufacturer (icc, NULL, data);
+
+ /* set the framework creator metadata */
+ cd_icc_add_metadata (icc,
+ CD_PROFILE_METADATA_CMF_PRODUCT,
+ PACKAGE_NAME);
+ cd_icc_add_metadata (icc,
+ CD_PROFILE_METADATA_CMF_BINARY,
+ PACKAGE_NAME);
+ cd_icc_add_metadata (icc,
+ CD_PROFILE_METADATA_CMF_VERSION,
+ PACKAGE_VERSION);
+ cd_icc_add_metadata (icc,
+ CD_PROFILE_METADATA_MAPPING_DEVICE_ID,
+ cd_device_get_id (device));
+
+ /* set 'ICC meta Tag for Monitor Profiles' data */
+ cd_icc_add_metadata (icc, CD_PROFILE_METADATA_EDID_MD5, ccm_edid_get_checksum (edid));
+ data = ccm_edid_get_monitor_name (edid);
+ if (data != NULL)
+ cd_icc_add_metadata (icc, CD_PROFILE_METADATA_EDID_MODEL, data);
+ data = ccm_edid_get_serial_number (edid);
+ if (data != NULL)
+ cd_icc_add_metadata (icc, CD_PROFILE_METADATA_EDID_SERIAL, data);
+ data = ccm_edid_get_pnp_id (edid);
+ if (data != NULL)
+ cd_icc_add_metadata (icc, CD_PROFILE_METADATA_EDID_MNFT, data);
+ data = ccm_edid_get_vendor_name (edid);
+ if (data != NULL)
+ cd_icc_add_metadata (icc, CD_PROFILE_METADATA_EDID_VENDOR, data);
+
+ /* save */
+ ret = cd_icc_save_file (icc, file, CD_ICC_SAVE_FLAGS_NONE, NULL, error);
+ if (!ret)
+ goto out;
+out:
+ if (icc != NULL)
+ g_object_unref (icc);
+ return ret;
+}
+
+static GPtrArray *
+ccm_session_generate_vcgt (CdProfile *profile, guint color_temperature, guint size)
+{
+ GnomeRROutputClutItem *tmp;
+ GPtrArray *array = NULL;
+ const cmsToneCurve **vcgt;
+ cmsFloat32Number in;
+ guint i;
+ cmsHPROFILE lcms_profile;
+ CdIcc *icc = NULL;
+ CdColorRGB temp;
+
+ /* invalid size */
+ if (size == 0)
+ goto out;
+
+ /* open file */
+ icc = cd_profile_load_icc (profile, CD_ICC_LOAD_FLAGS_NONE, NULL, NULL);
+ if (icc == NULL)
+ goto out;
+
+ /* get tone curves from profile */
+ lcms_profile = cd_icc_get_handle (icc);
+ vcgt = cmsReadTag (lcms_profile, cmsSigVcgtTag);
+ if (vcgt == NULL || vcgt[0] == NULL) {
+ g_debug ("profile does not have any VCGT data");
+ goto out;
+ }
+
+ /* get the color temperature */
+ if (!cd_color_get_blackbody_rgb_full (color_temperature,
+ &temp,
+ CD_COLOR_BLACKBODY_FLAG_USE_PLANCKIAN)) {
+ g_warning ("failed to get blackbody for %uK", color_temperature);
+ cd_color_rgb_set (&temp, 1.0, 1.0, 1.0);
+ } else {
+ g_debug ("using VCGT gamma of %uK = %.1f,%.1f,%.1f",
+ color_temperature, temp.R, temp.G, temp.B);
+ }
+
+ /* create array */
+ array = g_ptr_array_new_with_free_func (g_free);
+ for (i = 0; i < size; i++) {
+ in = (gdouble) i / (gdouble) (size - 1);
+ tmp = g_new0 (GnomeRROutputClutItem, 1);
+ tmp->red = cmsEvalToneCurveFloat(vcgt[0], in) * temp.R * (gdouble) 0xffff;
+ tmp->green = cmsEvalToneCurveFloat(vcgt[1], in) * temp.G * (gdouble) 0xffff;
+ tmp->blue = cmsEvalToneCurveFloat(vcgt[2], in) * temp.B * (gdouble) 0xffff;
+ g_ptr_array_add (array, tmp);
+ }
+out:
+ if (icc != NULL)
+ g_object_unref (icc);
+ return array;
+}
+
+static guint
+gnome_rr_output_get_gamma_size (GnomeRROutput *output)
+{
+ GnomeRRCrtc *crtc;
+ gint len = 0;
+
+ crtc = gnome_rr_output_get_crtc (output);
+ if (crtc == NULL)
+ return 0;
+ gnome_rr_crtc_get_gamma (crtc,
+ &len,
+ NULL, NULL, NULL);
+ return (guint) len;
+}
+
+static gboolean
+ccm_session_output_set_gamma (GnomeRROutput *output,
+ GPtrArray *array,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ guint16 *red = NULL;
+ guint16 *green = NULL;
+ guint16 *blue = NULL;
+ guint i;
+ GnomeRROutputClutItem *data;
+ GnomeRRCrtc *crtc;
+
+ /* no length? */
+ if (array->len == 0) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "no data in the CLUT array");
+ goto out;
+ }
+
+ /* convert to a type X understands */
+ red = g_new (guint16, array->len);
+ green = g_new (guint16, array->len);
+ blue = g_new (guint16, array->len);
+ for (i = 0; i < array->len; i++) {
+ data = g_ptr_array_index (array, i);
+ red[i] = data->red;
+ green[i] = data->green;
+ blue[i] = data->blue;
+ }
+
+ /* send to LUT */
+ crtc = gnome_rr_output_get_crtc (output);
+ if (crtc == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to get ctrc for %s",
+ gnome_rr_output_get_name (output));
+ goto out;
+ }
+ gnome_rr_crtc_set_gamma (crtc, array->len,
+ red, green, blue);
+out:
+ g_free (red);
+ g_free (green);
+ g_free (blue);
+ return ret;
+}
+
+static gboolean
+ccm_session_device_set_gamma (GnomeRROutput *output,
+ CdProfile *profile,
+ guint color_temperature,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ guint size;
+ GPtrArray *clut = NULL;
+
+ /* create a lookup table */
+ size = gnome_rr_output_get_gamma_size (output);
+ if (size == 0) {
+ ret = TRUE;
+ goto out;
+ }
+ clut = ccm_session_generate_vcgt (profile, color_temperature, size);
+ if (clut == NULL) {
+ g_set_error_literal (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to generate vcgt");
+ goto out;
+ }
+
+ /* apply the vcgt to this output */
+ ret = ccm_session_output_set_gamma (output, clut, error);
+ if (!ret)
+ goto out;
+out:
+ if (clut != NULL)
+ g_ptr_array_unref (clut);
+ return ret;
+}
+
+static gboolean
+ccm_session_device_reset_gamma (GnomeRROutput *output,
+ guint color_temperature,
+ GError **error)
+{
+ gboolean ret;
+ guint i;
+ guint size;
+ guint32 value;
+ GPtrArray *clut;
+ GnomeRROutputClutItem *data;
+ CdColorRGB temp;
+
+ /* create a linear ramp */
+ g_debug ("falling back to dummy ramp");
+ clut = g_ptr_array_new_with_free_func (g_free);
+ size = gnome_rr_output_get_gamma_size (output);
+ if (size == 0) {
+ ret = TRUE;
+ goto out;
+ }
+
+ /* get the color temperature */
+ if (!cd_color_get_blackbody_rgb_full (color_temperature,
+ &temp,
+ CD_COLOR_BLACKBODY_FLAG_USE_PLANCKIAN)) {
+ g_warning ("failed to get blackbody for %uK", color_temperature);
+ cd_color_rgb_set (&temp, 1.0, 1.0, 1.0);
+ } else {
+ g_debug ("using reset gamma of %uK = %.1f,%.1f,%.1f",
+ color_temperature, temp.R, temp.G, temp.B);
+ }
+
+ for (i = 0; i < size; i++) {
+ value = (i * 0xffff) / (size - 1);
+ data = g_new0 (GnomeRROutputClutItem, 1);
+ data->red = value * temp.R;
+ data->green = value * temp.G;
+ data->blue = value * temp.B;
+ g_ptr_array_add (clut, data);
+ }
+
+ /* apply the vcgt to this output */
+ ret = ccm_session_output_set_gamma (output, clut, error);
+ if (!ret)
+ goto out;
+out:
+ g_ptr_array_unref (clut);
+ return ret;
+}
+
+static GnomeRROutput *
+ccm_session_get_state_output_by_id (CsdColorState *state,
+ const gchar *device_id,
+ GError **error)
+{
+ gchar *output_id;
+ GnomeRROutput *output = NULL;
+ GnomeRROutput **outputs = NULL;
+ guint i;
+
+ /* search all STATE outputs for the device id */
+ outputs = gnome_rr_screen_list_outputs (state->state_screen);
+ if (outputs == NULL) {
+ g_set_error_literal (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "Failed to get outputs");
+ goto out;
+ }
+ for (i = 0; outputs[i] != NULL && output == NULL; i++) {
+ output_id = ccm_session_get_output_id (state, outputs[i]);
+ if (g_strcmp0 (output_id, device_id) == 0)
+ output = outputs[i];
+ g_free (output_id);
+ }
+ if (output == NULL) {
+ g_set_error (error,
+ CSD_COLOR_MANAGER_ERROR,
+ CSD_COLOR_MANAGER_ERROR_FAILED,
+ "Failed to find output %s",
+ device_id);
+ }
+out:
+ return output;
+}
+
+/* this function is more complicated than it should be, due to the
+ * fact that XOrg sometimes assigns no primary devices when using
+ * "xrandr --auto" or when the version of RANDR is < 1.3 */
+static gboolean
+ccm_session_use_output_profile_for_screen (CsdColorState *state,
+ GnomeRROutput *output)
+{
+ gboolean has_laptop = FALSE;
+ gboolean has_primary = FALSE;
+ GnomeRROutput **outputs;
+ GnomeRROutput *connected = NULL;
+ guint i;
+
+ /* do we have any screens marked as primary */
+ outputs = gnome_rr_screen_list_outputs (state->state_screen);
+ if (outputs == NULL || outputs[0] == NULL) {
+ g_warning ("failed to get outputs");
+ return FALSE;
+ }
+ for (i = 0; outputs[i] != NULL; i++) {
+ if (connected == NULL)
+ connected = outputs[i];
+ if (gnome_rr_output_get_is_primary (outputs[i]))
+ has_primary = TRUE;
+ if (gnome_rr_output_is_builtin_display (outputs[i]))
+ has_laptop = TRUE;
+ }
+
+ /* we have an assigned primary device, are we that? */
+ if (has_primary)
+ return gnome_rr_output_get_is_primary (output);
+
+ /* choosing the internal panel is probably sane */
+ if (has_laptop)
+ return gnome_rr_output_is_builtin_display (output);
+
+ /* we have to choose one, so go for the first connected device */
+ if (connected != NULL)
+ return gnome_rr_output_get_id (connected) == gnome_rr_output_get_id (output);
+
+ return FALSE;
+}
+
+/* TODO: remove when we can dep on a released version of colord */
+#ifndef CD_PROFILE_METADATA_SCREEN_BRIGHTNESS
+#define CD_PROFILE_METADATA_SCREEN_BRIGHTNESS "SCREEN_brightness"
+#endif
+
+#define CSD_DBUS_NAME_POWER CSD_DBUS_NAME ".Power"
+#define CSD_DBUS_INTERFACE_POWER_SCREEN CSD_DBUS_BASE_INTERFACE ".Power.Screen"
+#define CSD_DBUS_PATH_POWER CSD_DBUS_PATH "/Power"
+
+static void
+ccm_session_set_output_percentage (guint percentage)
+{
+ GDBusConnection *connection;
+
+ /* get a ref to the existing bus connection */
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ if (connection == NULL)
+ return;
+ g_dbus_connection_call (connection,
+ CSD_DBUS_NAME_POWER,
+ CSD_DBUS_PATH_POWER,
+ "org.freedesktop.DBus.Properties",
+ "Set",
+ g_variant_new_parsed ("('" CSD_DBUS_INTERFACE_POWER_SCREEN "',"
+ "'Brightness', %v)",
+ g_variant_new_int32 (percentage)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+ g_object_unref (connection);
+}
+
+static void
+ccm_session_device_assign_profile_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdProfile *profile = CD_PROFILE (object);
+ const gchar *brightness_profile;
+ const gchar *filename;
+ gboolean ret;
+ GError *error = NULL;
+ GnomeRROutput *output;
+ guint brightness_percentage;
+ CcmSessionAsyncHelper *helper = (CcmSessionAsyncHelper *) user_data;
+ CsdColorState *state = CSD_COLOR_STATE (helper->state);
+
+ /* get properties */
+ ret = cd_profile_connect_finish (profile, res, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to connect to profile: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* get the filename */
+ filename = cd_profile_get_filename (profile);
+ g_assert (filename != NULL);
+
+ /* get the output (can't save in helper as GnomeRROutput isn't
+ * a GObject, just a pointer */
+ output = gnome_rr_screen_get_output_by_id (state->state_screen,
+ helper->output_id);
+ if (output == NULL)
+ goto out;
+
+ /* if output is a laptop screen and the profile has a
+ * calibration brightness then set this new brightness */
+ brightness_profile = cd_profile_get_metadata_item (profile,
+ CD_PROFILE_METADATA_SCREEN_BRIGHTNESS);
+ if (gnome_rr_output_is_builtin_display (output) &&
+ brightness_profile != NULL) {
+ /* the percentage is stored in the profile metadata as
+ * a string, not ideal, but it's all we have... */
+ brightness_percentage = atoi (brightness_profile);
+ ccm_session_set_output_percentage (brightness_percentage);
+ }
+
+ /* set the _ICC_PROFILE atom */
+ ret = ccm_session_use_output_profile_for_screen (state, output);
+ if (ret) {
+ ret = ccm_session_screen_set_icc_profile (state,
+ filename,
+ &error);
+ if (!ret) {
+ g_warning ("failed to set screen _ICC_PROFILE: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* create a vcgt for this icc file */
+ ret = cd_profile_get_has_vcgt (profile);
+ if (ret) {
+ ret = ccm_session_device_set_gamma (output,
+ profile,
+ state->color_temperature,
+ &error);
+ if (!ret) {
+ g_warning ("failed to set %s gamma tables: %s",
+ cd_device_get_id (helper->device),
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+ } else {
+ ret = ccm_session_device_reset_gamma (output,
+ state->color_temperature,
+ &error);
+ if (!ret) {
+ g_warning ("failed to reset %s gamma tables: %s",
+ cd_device_get_id (helper->device),
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+ }
+out:
+ ccm_session_async_helper_free (helper);
+}
+
+/*
+ * Check to see if the on-disk profile has the MAPPING_device_id
+ * metadata, and if not, we should delete the profile and re-create it
+ * so that it gets mapped by the daemon.
+ */
+static gboolean
+ccm_session_check_profile_device_md (GFile *file)
+{
+ const gchar *key_we_need = CD_PROFILE_METADATA_MAPPING_DEVICE_ID;
+ CdIcc *icc;
+ gboolean ret;
+
+ icc = cd_icc_new ();
+ ret = cd_icc_load_file (icc, file, CD_ICC_LOAD_FLAGS_METADATA, NULL, NULL);
+ if (!ret)
+ goto out;
+ ret = cd_icc_get_metadata_item (icc, key_we_need) != NULL;
+ if (!ret) {
+ g_debug ("auto-edid profile is old, and contains no %s data",
+ key_we_need);
+ }
+out:
+ g_object_unref (icc);
+ return ret;
+}
+
+static void
+ccm_session_device_assign_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdDeviceKind kind;
+ CdProfile *profile = NULL;
+ gboolean ret;
+ gchar *autogen_filename = NULL;
+ gchar *autogen_path = NULL;
+ CcmEdid *edid = NULL;
+ GnomeRROutput *output = NULL;
+ GError *error = NULL;
+ GFile *file = NULL;
+ const gchar *xrandr_id;
+ CcmSessionAsyncHelper *helper;
+ CdDevice *device = CD_DEVICE (object);
+ CsdColorState *state = CSD_COLOR_STATE (user_data);
+
+ /* remove from assign array */
+ g_hash_table_remove (state->device_assign_hash,
+ cd_device_get_object_path (device));
+
+ /* get properties */
+ ret = cd_device_connect_finish (device, res, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to connect to device: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* check we care */
+ kind = cd_device_get_kind (device);
+ if (kind != CD_DEVICE_KIND_DISPLAY)
+ goto out;
+
+ g_debug ("need to assign display device %s",
+ cd_device_get_id (device));
+
+ /* get the GnomeRROutput for the device id */
+ xrandr_id = cd_device_get_id (device);
+ output = ccm_session_get_state_output_by_id (state,
+ xrandr_id,
+ &error);
+ if (output == NULL) {
+ g_warning ("no %s device found: %s",
+ cd_device_get_id (device),
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* create profile from device edid if it exists */
+ edid = ccm_session_get_output_edid (state, output, &error);
+ if (edid == NULL) {
+ g_warning ("unable to get EDID for %s: %s",
+ cd_device_get_id (device),
+ error->message);
+ g_clear_error (&error);
+
+ } else {
+ autogen_filename = g_strdup_printf ("edid-%s.icc",
+ ccm_edid_get_checksum (edid));
+ autogen_path = g_build_filename (g_get_user_data_dir (),
+ "icc", autogen_filename, NULL);
+
+ /* check if auto-profile has up-to-date metadata */
+ file = g_file_new_for_path (autogen_path);
+ if (ccm_session_check_profile_device_md (file)) {
+ g_debug ("auto-profile edid %s exists with md", autogen_path);
+ } else {
+ g_debug ("auto-profile edid does not exist, creating as %s",
+ autogen_path);
+
+ /* check if the system has a built-in profile */
+ ret = gnome_rr_output_is_builtin_display (output) &&
+ ccm_get_system_icc_profile (state, file);
+
+ /* try creating one from the EDID */
+ if (!ret) {
+ ret = ccm_apply_create_icc_profile_for_edid (state,
+ device,
+ edid,
+ file,
+ &error);
+ }
+
+ if (!ret) {
+ g_warning ("failed to create profile from EDID data: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+ }
+
+ /* get the default profile for the device */
+ profile = cd_device_get_default_profile (device);
+ if (profile == NULL) {
+ g_debug ("%s has no default profile to set",
+ cd_device_get_id (device));
+
+ /* the default output? */
+ if (gnome_rr_output_get_is_primary (output) &&
+ state->gdk_window != NULL) {
+ gdk_property_delete (state->gdk_window,
+ gdk_atom_intern_static_string ("_ICC_PROFILE"));
+ gdk_property_delete (state->gdk_window,
+ gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION"));
+ }
+
+ /* reset, as we want linear profiles for profiling */
+ ret = ccm_session_device_reset_gamma (output,
+ state->color_temperature,
+ &error);
+ if (!ret) {
+ g_warning ("failed to reset %s gamma tables: %s",
+ cd_device_get_id (device),
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+ goto out;
+ }
+
+ /* get properties */
+ helper = g_new0 (CcmSessionAsyncHelper, 1);
+ helper->output_id = gnome_rr_output_get_id (output);
+ helper->state = g_object_ref (state);
+ helper->device = g_object_ref (device);
+ cd_profile_connect (profile,
+ state->cancellable,
+ ccm_session_device_assign_profile_connect_cb,
+ helper);
+out:
+ g_free (autogen_filename);
+ g_free (autogen_path);
+ if (file != NULL)
+ g_object_unref (file);
+ if (edid != NULL)
+ g_object_unref (edid);
+ if (profile != NULL)
+ g_object_unref (profile);
+}
+
+static void
+ccm_session_device_assign (CsdColorState *state, CdDevice *device)
+{
+ const gchar *key;
+ gpointer found;
+
+ /* are we already assigning this device */
+ key = cd_device_get_object_path (device);
+ found = g_hash_table_lookup (state->device_assign_hash, key);
+ if (found != NULL) {
+ g_debug ("assign for %s already in progress", key);
+ return;
+ }
+ g_hash_table_insert (state->device_assign_hash,
+ g_strdup (key),
+ GINT_TO_POINTER (TRUE));
+ cd_device_connect (device,
+ state->cancellable,
+ ccm_session_device_assign_connect_cb,
+ state);
+}
+
+static void
+ccm_session_device_added_assign_cb (CdClient *client,
+ CdDevice *device,
+ CsdColorState *state)
+{
+ ccm_session_device_assign (state, device);
+}
+
+static void
+ccm_session_device_changed_assign_cb (CdClient *client,
+ CdDevice *device,
+ CsdColorState *state)
+{
+ g_debug ("%s changed", cd_device_get_object_path (device));
+ ccm_session_device_assign (state, device);
+}
+
+static void
+ccm_session_create_device_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdDevice *device;
+ GError *error = NULL;
+
+ device = cd_client_create_device_finish (CD_CLIENT (object),
+ res,
+ &error);
+ if (device == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ !g_error_matches (error, CD_CLIENT_ERROR, CD_CLIENT_ERROR_ALREADY_EXISTS))
+ g_warning ("failed to create device: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ g_object_unref (device);
+}
+
+static void
+ccm_session_add_state_output (CsdColorState *state, GnomeRROutput *output)
+{
+ const gchar *edid_checksum = NULL;
+ const gchar *model = NULL;
+ const gchar *output_name = NULL;
+ const gchar *serial = NULL;
+ const gchar *vendor = NULL;
+ gboolean ret;
+ gchar *device_id = NULL;
+ CcmEdid *edid;
+ GError *error = NULL;
+ GHashTable *device_props = NULL;
+
+ /* VNC creates a fake device that cannot be color managed */
+ output_name = gnome_rr_output_get_name (output);
+ if (output_name != NULL && g_str_has_prefix (output_name, "VNC-")) {
+ g_debug ("ignoring %s as fake VNC device detected", output_name);
+ return;
+ }
+
+ /* try to get edid */
+ edid = ccm_session_get_output_edid (state, output, &error);
+ if (edid == NULL) {
+ g_warning ("failed to get edid: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+
+ /* prefer DMI data for the internal output */
+ ret = gnome_rr_output_is_builtin_display (output);
+ if (ret) {
+ model = cd_client_get_system_model (state->client);
+ vendor = cd_client_get_system_vendor (state->client);
+ }
+
+ /* use EDID data if we have it */
+ if (edid != NULL) {
+ edid_checksum = ccm_edid_get_checksum (edid);
+ if (model == NULL)
+ model = ccm_edid_get_monitor_name (edid);
+ if (vendor == NULL)
+ vendor = ccm_edid_get_vendor_name (edid);
+ if (serial == NULL)
+ serial = ccm_edid_get_serial_number (edid);
+ }
+
+ /* ensure mandatory fields are set */
+ if (model == NULL)
+ model = gnome_rr_output_get_name (output);
+ if (vendor == NULL)
+ vendor = "unknown";
+ if (serial == NULL)
+ serial = "unknown";
+
+ device_id = ccm_session_get_output_id (state, output);
+ g_debug ("output %s added", device_id);
+ device_props = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, NULL);
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_KIND,
+ (gpointer) cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY));
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_MODE,
+ (gpointer) cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL));
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_COLORSPACE,
+ (gpointer) cd_colorspace_to_string (CD_COLORSPACE_RGB));
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_VENDOR,
+ (gpointer) vendor);
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_MODEL,
+ (gpointer) model);
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_SERIAL,
+ (gpointer) serial);
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_METADATA_XRANDR_NAME,
+ (gpointer) gnome_rr_output_get_name (output));
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY,
+ gnome_rr_output_get_is_primary (output) ?
+ (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_PRIMARY :
+ (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_SECONDARY);
+ if (edid_checksum != NULL) {
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_METADATA_OUTPUT_EDID_MD5,
+ (gpointer) edid_checksum);
+ }
+ /* set this so we can call the device a 'Laptop Screen' in the
+ * control center main panel */
+ if (gnome_rr_output_is_builtin_display (output)) {
+ g_hash_table_insert (device_props,
+ (gpointer) CD_DEVICE_PROPERTY_EMBEDDED,
+ NULL);
+ }
+ cd_client_create_device (state->client,
+ device_id,
+ CD_OBJECT_SCOPE_TEMP,
+ device_props,
+ state->cancellable,
+ ccm_session_create_device_cb,
+ state);
+ g_free (device_id);
+ if (device_props != NULL)
+ g_hash_table_unref (device_props);
+ if (edid != NULL)
+ g_object_unref (edid);
+}
+
+
+static void
+gnome_rr_screen_output_added_cb (GnomeRRScreen *screen,
+ GnomeRROutput *output,
+ CsdColorState *state)
+{
+ ccm_session_add_state_output (state, output);
+}
+
+static void
+ccm_session_screen_removed_delete_device_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+
+ /* deleted device */
+ ret = cd_client_delete_device_finish (CD_CLIENT (object),
+ res,
+ &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to delete device: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+ccm_session_screen_removed_find_device_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ CdDevice *device;
+ CsdColorState *state = CSD_COLOR_STATE (user_data);
+
+ device = cd_client_find_device_finish (state->client,
+ res,
+ &error);
+ if (device == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to find device: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ g_debug ("output %s found, and will be removed",
+ cd_device_get_object_path (device));
+ cd_client_delete_device (state->client,
+ device,
+ state->cancellable,
+ ccm_session_screen_removed_delete_device_cb,
+ state);
+ g_object_unref (device);
+}
+
+static void
+gnome_rr_screen_output_removed_cb (GnomeRRScreen *screen,
+ GnomeRROutput *output,
+ CsdColorState *state)
+{
+ g_debug ("output %s removed",
+ gnome_rr_output_get_name (output));
+ g_hash_table_remove (state->edid_cache,
+ gnome_rr_output_get_name (output));
+ cd_client_find_device_by_property (state->client,
+ CD_DEVICE_METADATA_XRANDR_NAME,
+ gnome_rr_output_get_name (output),
+ state->cancellable,
+ ccm_session_screen_removed_find_device_cb,
+ state);
+}
+
+static void
+ccm_session_get_devices_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ CdDevice *device;
+ GError *error = NULL;
+ GPtrArray *array;
+ guint i;
+ CsdColorState *state = CSD_COLOR_STATE (user_data);
+
+ array = cd_client_get_devices_finish (CD_CLIENT (object), res, &error);
+ if (array == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to get devices: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ for (i = 0; i < array->len; i++) {
+ device = g_ptr_array_index (array, i);
+ ccm_session_device_assign (state, device);
+ }
+
+ if (array != NULL)
+ g_ptr_array_unref (array);
+}
+
+static void
+ccm_session_profile_gamma_find_device_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdClient *client = CD_CLIENT (object);
+ CdDevice *device = NULL;
+ GError *error = NULL;
+ CsdColorState *state = CSD_COLOR_STATE (user_data);
+
+ device = cd_client_find_device_by_property_finish (client,
+ res,
+ &error);
+ if (device == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("could not find device: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* get properties */
+ cd_device_connect (device,
+ state->cancellable,
+ ccm_session_device_assign_connect_cb,
+ state);
+
+ if (device != NULL)
+ g_object_unref (device);
+}
+
+static void
+ccm_session_set_gamma_for_all_devices (CsdColorState *state)
+{
+ GnomeRROutput **outputs;
+ guint i;
+
+ /* setting the temperature before we get the list of devices is fine,
+ * as we use the temperature in the calculation */
+ if (state->state_screen == NULL)
+ return;
+
+ /* get STATE outputs */
+ outputs = gnome_rr_screen_list_outputs (state->state_screen);
+ if (outputs == NULL) {
+ g_warning ("failed to get outputs");
+ return;
+ }
+ for (i = 0; outputs[i] != NULL; i++) {
+ /* get CdDevice for this output */
+ cd_client_find_device_by_property (state->client,
+ CD_DEVICE_METADATA_XRANDR_NAME,
+ gnome_rr_output_get_name (outputs[i]),
+ state->cancellable,
+ ccm_session_profile_gamma_find_device_cb,
+ state);
+ }
+}
+
+/* We have to reset the gamma tables each time as if the primary output
+ * has changed then different crtcs are going to be used.
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=660164 for an example */
+static void
+gnome_rr_screen_output_changed_cb (GnomeRRScreen *screen,
+ CsdColorState *state)
+{
+ ccm_session_set_gamma_for_all_devices (state);
+}
+
+static gboolean
+has_changed (char **strv,
+ const char *str)
+{
+ guint i;
+ for (i = 0; strv[i] != NULL; i++) {
+ if (g_str_equal (str, strv[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+ccm_session_active_changed_cb (GDBusProxy *session,
+ GVariant *changed,
+ char **invalidated,
+ CsdColorState *state)
+{
+ GVariant *active_v = NULL;
+ gboolean is_active;
+
+ if (has_changed (invalidated, "SessionIsActive"))
+ return;
+
+ /* not yet connected to the daemon */
+ if (!cd_client_get_connected (state->client))
+ return;
+
+ active_v = g_dbus_proxy_get_cached_property (session, "SessionIsActive");
+ g_return_if_fail (active_v != NULL);
+ is_active = g_variant_get_boolean (active_v);
+ g_variant_unref (active_v);
+
+ /* When doing the fast-user-switch into a new account, load the
+ * new users chosen profiles.
+ *
+ * If this is the first time the GnomeSettingsSession has been
+ * loaded, then we'll get a change from unknown to active
+ * and we want to avoid reprobing the devices for that.
+ */
+ if (is_active && !state->session_is_active) {
+ g_debug ("Done switch to new account, reload devices");
+ cd_client_get_devices (state->client,
+ state->cancellable,
+ ccm_session_get_devices_cb,
+ state);
+ }
+ state->session_is_active = is_active;
+}
+
+static void
+ccm_session_client_connect_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error = NULL;
+ GnomeRROutput **outputs;
+ guint i;
+ CsdColorState *state = CSD_COLOR_STATE (user_data);
+
+ /* connected */
+ ret = cd_client_connect_finish (state->client, res, &error);
+ if (!ret) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to connect to colord: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* is there an available colord instance? */
+ ret = cd_client_get_has_server (state->client);
+ if (!ret) {
+ g_warning ("There is no colord server available");
+ return;
+ }
+
+ /* watch if sessions change */
+ g_signal_connect_object (state->session, "g-properties-changed",
+ G_CALLBACK (ccm_session_active_changed_cb),
+ state, 0);
+
+ /* add screens */
+ gnome_rr_screen_refresh (state->state_screen, &error);
+ if (error != NULL) {
+ g_warning ("failed to refresh: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* get STATE outputs */
+ outputs = gnome_rr_screen_list_outputs (state->state_screen);
+ if (outputs == NULL) {
+ g_warning ("failed to get outputs");
+ return;
+ }
+ for (i = 0; outputs[i] != NULL; i++) {
+ ccm_session_add_state_output (state, outputs[i]);
+ }
+
+ /* only connect when colord is awake */
+ g_signal_connect (state->state_screen, "output-connected",
+ G_CALLBACK (gnome_rr_screen_output_added_cb),
+ state);
+ g_signal_connect (state->state_screen, "output-disconnected",
+ G_CALLBACK (gnome_rr_screen_output_removed_cb),
+ state);
+ g_signal_connect (state->state_screen, "changed",
+ G_CALLBACK (gnome_rr_screen_output_changed_cb),
+ state);
+
+ g_signal_connect (state->client, "device-added",
+ G_CALLBACK (ccm_session_device_added_assign_cb),
+ state);
+ g_signal_connect (state->client, "device-changed",
+ G_CALLBACK (ccm_session_device_changed_assign_cb),
+ state);
+
+ /* set for each device that already exist */
+ cd_client_get_devices (state->client,
+ state->cancellable,
+ ccm_session_get_devices_cb,
+ state);
+}
+
+static void
+on_rr_screen_acquired (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ CsdColorState *state = data;
+ GnomeRRScreen *screen;
+ GError *error = NULL;
+
+ /* gnome_rr_screen_new_async() does not take a GCancellable */
+ if (g_cancellable_is_cancelled (state->cancellable))
+ goto out;
+
+ screen = gnome_rr_screen_new_finish (result, &error);
+ if (screen == NULL) {
+ g_warning ("failed to get screens: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ state->state_screen = screen;
+
+ cd_client_connect (state->client,
+ state->cancellable,
+ ccm_session_client_connect_cb,
+ state);
+out:
+ /* manually added */
+ g_object_unref (state);
+}
+
+void
+csd_color_state_start (CsdColorState *state)
+{
+ /* use a fresh cancellable for each start->stop operation */
+ g_cancellable_cancel (state->cancellable);
+ g_clear_object (&state->cancellable);
+ state->cancellable = g_cancellable_new ();
+
+ /* coldplug the list of screens */
+ gnome_rr_screen_new_async (gdk_screen_get_default (),
+ on_rr_screen_acquired,
+ g_object_ref (state));
+}
+
+void
+csd_color_state_stop (CsdColorState *state)
+{
+ g_cancellable_cancel (state->cancellable);
+}
+
+static void
+csd_color_state_class_init (CsdColorStateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = csd_color_state_finalize;
+}
+
+static void
+csd_color_state_init (CsdColorState *state)
+{
+ /* track the active session */
+ state->session = gnome_session_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ "org.gnome.SessionManager",
+ "/org/gnome/SessionManager",
+ NULL,
+ NULL);
+
+#ifdef GDK_WINDOWING_X11
+ /* set the _ICC_PROFILE atoms on the root screen */
+ if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+ state->gdk_window = gdk_screen_get_root_window (gdk_screen_get_default ());
+#endif
+
+ /* parsing the EDID is expensive */
+ state->edid_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ /* we don't want to assign devices multiple times at startup */
+ state->device_assign_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ /* default color temperature */
+ state->color_temperature = CSD_COLOR_TEMPERATURE_DEFAULT;
+
+ state->client = cd_client_new ();
+}
+
+static void
+csd_color_state_finalize (GObject *object)
+{
+ CsdColorState *state;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CSD_IS_COLOR_STATE (object));
+
+ state = CSD_COLOR_STATE (object);
+
+ g_cancellable_cancel (state->cancellable);
+ g_clear_object (&state->cancellable);
+ g_clear_object (&state->client);
+ g_clear_object (&state->session);
+ g_clear_pointer (&state->edid_cache, g_hash_table_destroy);
+ g_clear_pointer (&state->device_assign_hash, g_hash_table_destroy);
+ g_clear_object (&state->state_screen);
+
+ G_OBJECT_CLASS (csd_color_state_parent_class)->finalize (object);
+}
+
+CsdColorState *
+csd_color_state_new (void)
+{
+ CsdColorState *state;
+ state = g_object_new (CSD_TYPE_COLOR_STATE, NULL);
+ return CSD_COLOR_STATE (state);
+}
diff --git a/plugins/color/csd-color-state.h b/plugins/color/csd-color-state.h
new file mode 100644
index 00000000..608bf237
--- /dev/null
+++ b/plugins/color/csd-color-state.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+ * Copyright (C) 2011-2013 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#ifndef __CSD_COLOR_STATE_H
+#define __CSD_COLOR_STATE_H
+
+#include
+
+G_BEGIN_DECLS
+
+#define CSD_TYPE_COLOR_STATE (csd_color_state_get_type ())
+
+G_DECLARE_FINAL_TYPE (CsdColorState, csd_color_state, CSD, COLOR_STATE, GObject)
+
+#define CSD_COLOR_TEMPERATURE_MIN 1000 /* Kelvin */
+#define CSD_COLOR_TEMPERATURE_DEFAULT 6500 /* Kelvin, is RGB [1.0,1.0,1.0] */
+#define CSD_COLOR_TEMPERATURE_MAX 10000 /* Kelvin */
+
+GQuark csd_color_state_error_quark (void);
+
+CsdColorState * csd_color_state_new (void);
+void csd_color_state_start (CsdColorState *state);
+void csd_color_state_stop (CsdColorState *state);
+void csd_color_state_set_temperature (CsdColorState *state,
+ guint temperature);
+guint csd_color_state_get_temperature (CsdColorState *state);
+
+G_END_DECLS
+
+#endif /* __CSD_COLOR_STATE_H */
diff --git a/plugins/color/csd-night-light-common.c b/plugins/color/csd-night-light-common.c
new file mode 100644
index 00000000..6710e5c0
--- /dev/null
+++ b/plugins/color/csd-night-light-common.c
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "csd-night-light-common.h"
+
+static gdouble
+deg2rad (gdouble degrees)
+{
+ return (M_PI * degrees) / 180.f;
+}
+
+static gdouble
+rad2deg (gdouble radians)
+{
+ return radians * (180.f / M_PI);
+}
+
+/*
+ * Formulas taken from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
+ *
+ * The returned values are fractional hours, so 6am would be 6.0 and 4:30pm
+ * would be 16.5.
+ *
+ * The values returned by this function might not make sense for locations near
+ * the polar regions. For example, in the north of Lapland there might not be
+ * a sunrise at all.
+ */
+gboolean
+csd_night_light_get_sunrise_sunset (GDateTime *dt,
+ gdouble pos_lat, gdouble pos_long,
+ gdouble *sunrise, gdouble *sunset)
+{
+ g_autoptr(GDateTime) dt_zero = g_date_time_new_utc (1900, 1, 1, 0, 0, 0);
+ GTimeSpan ts = g_date_time_difference (dt, dt_zero);
+
+ g_return_val_if_fail (pos_lat <= 90.f && pos_lat >= -90.f, FALSE);
+ g_return_val_if_fail (pos_long <= 180.f && pos_long >= -180.f, FALSE);
+
+ gdouble tz_offset = (gdouble) g_date_time_get_utc_offset (dt) / G_USEC_PER_SEC / 60 / 60; // B5
+ gdouble date_as_number = ts / G_USEC_PER_SEC / 24 / 60 / 60 + 2; // B7
+ gdouble time_past_local_midnight = 0; // E2, unused in this calculation
+ gdouble julian_day = date_as_number + 2415018.5 +
+ time_past_local_midnight - tz_offset / 24;
+ gdouble julian_century = (julian_day - 2451545) / 36525;
+ gdouble geom_mean_long_sun = fmod (280.46646 + julian_century *
+ (36000.76983 + julian_century * 0.0003032), 360); // I2
+ gdouble geom_mean_anom_sun = 357.52911 + julian_century *
+ (35999.05029 - 0.0001537 * julian_century); // J2
+ gdouble eccent_earth_orbit = 0.016708634 - julian_century *
+ (0.000042037 + 0.0000001267 * julian_century); // K2
+ gdouble sun_eq_of_ctr = sin (deg2rad (geom_mean_anom_sun)) *
+ (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) +
+ sin (deg2rad (2 * geom_mean_anom_sun)) * (0.019993 - 0.000101 * julian_century) +
+ sin (deg2rad (3 * geom_mean_anom_sun)) * 0.000289; // L2
+ gdouble sun_true_long = geom_mean_long_sun + sun_eq_of_ctr; // M2
+ gdouble sun_app_long = sun_true_long - 0.00569 - 0.00478 *
+ sin (deg2rad (125.04 - 1934.136 * julian_century)); // P2
+ gdouble mean_obliq_ecliptic = 23 + (26 + ((21.448 - julian_century *
+ (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60; // Q2
+ gdouble obliq_corr = mean_obliq_ecliptic + 0.00256 *
+ cos (deg2rad (125.04 - 1934.136 * julian_century)); // R2
+ gdouble sun_declin = rad2deg (asin (sin (deg2rad (obliq_corr)) *
+ sin (deg2rad (sun_app_long)))); // T2
+ gdouble var_y = tan (deg2rad (obliq_corr/2)) * tan (deg2rad (obliq_corr / 2)); // U2
+ gdouble eq_of_time = 4 * rad2deg (var_y * sin (2 * deg2rad (geom_mean_long_sun)) -
+ 2 * eccent_earth_orbit * sin (deg2rad (geom_mean_anom_sun)) +
+ 4 * eccent_earth_orbit * var_y *
+ sin (deg2rad (geom_mean_anom_sun)) *
+ cos (2 * deg2rad (geom_mean_long_sun)) -
+ 0.5 * var_y * var_y * sin (4 * deg2rad (geom_mean_long_sun)) -
+ 1.25 * eccent_earth_orbit * eccent_earth_orbit *
+ sin (2 * deg2rad (geom_mean_anom_sun))); // V2
+ gdouble ha_sunrise = rad2deg (acos (cos (deg2rad (90.833)) / (cos (deg2rad (pos_lat)) *
+ cos (deg2rad (sun_declin))) - tan (deg2rad (pos_lat)) *
+ tan (deg2rad (sun_declin)))); // W2
+ gdouble solar_noon = (720 - 4 * pos_long - eq_of_time + tz_offset * 60) / 1440; // X2
+ gdouble sunrise_time = solar_noon - ha_sunrise * 4 / 1440; // Y2
+ gdouble sunset_time = solar_noon + ha_sunrise * 4 / 1440; // Z2
+
+ /* convert to hours */
+ if (sunrise != NULL)
+ *sunrise = sunrise_time * 24;
+ if (sunset != NULL)
+ *sunset = sunset_time * 24;
+ return TRUE;
+}
+
+gdouble
+csd_night_light_frac_day_from_dt (GDateTime *dt)
+{
+ return g_date_time_get_hour (dt) +
+ (gdouble) g_date_time_get_minute (dt) / 60.f +
+ (gdouble) g_date_time_get_second (dt) / 3600.f;
+}
+
+gchar *
+csd_night_light_time_string_from_frac (gdouble fraction)
+{
+ g_autoptr(GDateTime) dt = g_date_time_new_local (2000, 1, 1, (int) trunc (fraction), (int) ((fraction - trunc(fraction)) * 60.0f), 0);
+ return g_date_time_format (dt, "%H:%M");
+}
+
+gboolean
+csd_night_light_frac_day_is_between (gdouble value,
+ gdouble start,
+ gdouble end)
+{
+ /* wrap end to the next day if it is before start,
+ * considering equal values as a full 24h period
+ */
+ if (end <= start)
+ end += 24;
+
+ /* wrap value to the next day if it is before the range */
+ if (value < start && value < end)
+ value += 24;
+
+ /* Check whether value falls into range; together with the 24h
+ * wrap around above this means that TRUE is always returned when
+ * start == end.
+ */
+ return value >= start && value < end;
+}
diff --git a/plugins/color/csd-night-light-common.h b/plugins/color/csd-night-light-common.h
new file mode 100644
index 00000000..f095e8d9
--- /dev/null
+++ b/plugins/color/csd-night-light-common.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Richard Hughes
+ *
+ * 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 .
+ *
+ */
+
+#ifndef __CSD_NIGHT_LIGHT_COMMON_H
+#define __CSD_NIGHT_LIGHT_COMMON_H
+
+#include
+
+G_BEGIN_DECLS
+
+gboolean csd_night_light_get_sunrise_sunset (GDateTime *dt,
+ gdouble pos_lat,
+ gdouble pos_long,
+ gdouble *sunrise,
+ gdouble *sunset);
+gdouble csd_night_light_frac_day_from_dt (GDateTime *dt);
+gchar * csd_night_light_time_string_from_frac (gdouble fraction);
+gboolean csd_night_light_frac_day_is_between (gdouble value,
+ gdouble start,
+ gdouble end);
+
+G_END_DECLS
+
+#endif /* __CSD_NIGHT_LIGHT_COMMON_H */
diff --git a/plugins/color/csd-night-light.c b/plugins/color/csd-night-light.c
new file mode 100644
index 00000000..5b87be94
--- /dev/null
+++ b/plugins/color/csd-night-light.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2017 Richard Hughes
+ *
+ * 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"
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-datetime-source.h"
+
+#include "csd-color-state.h"
+
+#include "csd-night-light.h"
+#include "csd-night-light-common.h"
+#include "tz-coords.h"
+
+struct _CsdNightLight {
+ GObject parent;
+ GSettings *settings;
+ gboolean forced;
+ gboolean disabled_until_tmw;
+ GDateTime *disabled_until_tmw_dt;
+ GSource *source;
+ guint validate_id;
+ gdouble cached_sunrise;
+ gdouble cached_sunset;
+ gdouble cached_temperature;
+ gboolean cached_active;
+ gboolean smooth_enabled;
+ GTimer *smooth_timer;
+ guint smooth_id;
+ gdouble smooth_target_temperature;
+ GCancellable *cancellable;
+ GDateTime *datetime_override;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_SUNRISE,
+ PROP_SUNSET,
+ PROP_TEMPERATURE,
+ PROP_DISABLED_UNTIL_TMW,
+ PROP_FORCED,
+ PROP_LAST
+};
+
+#define CSD_NIGHT_LIGHT_SCHEDULE_TIMEOUT 5 /* seconds */
+#define CSD_NIGHT_LIGHT_POLL_TIMEOUT 60 /* seconds */
+#define CSD_NIGHT_LIGHT_POLL_SMEAR 1 /* hours */
+#define CSD_NIGHT_LIGHT_SMOOTH_SMEAR 5.f /* seconds */
+
+#define CSD_FRAC_DAY_MAX_DELTA (1.f/60.f) /* 1 minute */
+#define CSD_TEMPERATURE_MAX_DELTA (10.f) /* Kelvin */
+
+#define DESKTOP_ID "gnome-color-panel"
+
+static void poll_timeout_destroy (CsdNightLight *self);
+static void poll_timeout_create (CsdNightLight *self);
+static void night_light_recheck (CsdNightLight *self);
+
+G_DEFINE_TYPE (CsdNightLight, csd_night_light, G_TYPE_OBJECT);
+
+static GDateTime *
+csd_night_light_get_date_time_now (CsdNightLight *self)
+{
+ if (self->datetime_override != NULL)
+ return g_date_time_ref (self->datetime_override);
+ return g_date_time_new_now_local ();
+}
+
+void
+csd_night_light_set_date_time_now (CsdNightLight *self, GDateTime *datetime)
+{
+ if (self->datetime_override != NULL)
+ g_date_time_unref (self->datetime_override);
+ self->datetime_override = g_date_time_ref (datetime);
+
+ night_light_recheck (self);
+}
+
+static void
+poll_smooth_destroy (CsdNightLight *self)
+{
+ if (self->smooth_id != 0) {
+ g_source_remove (self->smooth_id);
+ self->smooth_id = 0;
+ }
+ if (self->smooth_timer != NULL)
+ g_clear_pointer (&self->smooth_timer, g_timer_destroy);
+}
+
+void
+csd_night_light_set_smooth_enabled (CsdNightLight *self,
+ gboolean smooth_enabled)
+{
+ /* ensure the timeout is stopped if called at runtime */
+ if (!smooth_enabled)
+ poll_smooth_destroy (self);
+ self->smooth_enabled = smooth_enabled;
+}
+
+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 (CsdNightLight *self)
+{
+ gboolean ret = FALSE;
+ gdouble latitude;
+ gdouble longitude;
+ gdouble sunrise;
+ gdouble sunset;
+ g_autoptr(GVariant) tmp = NULL;
+ g_autoptr(GDateTime) dt_now = csd_night_light_get_date_time_now (self);
+
+ /* calculate the sunrise/sunset for the location */
+ tmp = g_settings_get_value (self->settings, "night-light-last-coordinates");
+ g_variant_get (tmp, "(dd)", &latitude, &longitude);
+ if (latitude > 90.f || latitude < -90.f)
+ return FALSE;
+ if (longitude > 180.f || longitude < -180.f)
+ return FALSE;
+ if (!csd_night_light_get_sunrise_sunset (dt_now, latitude, longitude,
+ &sunrise, &sunset)) {
+ g_warning ("failed to get sunset/sunrise for %.3f,%.3f",
+ latitude, longitude);
+ return FALSE;
+ }
+
+ /* anything changed */
+ if (ABS (self->cached_sunrise - sunrise) > CSD_FRAC_DAY_MAX_DELTA) {
+ self->cached_sunrise = sunrise;
+ g_object_notify (G_OBJECT (self), "sunrise");
+ g_autofree gchar *formatted = csd_night_light_time_string_from_frac (sunrise);
+ g_debug ("Sunrise updated: %.3f (%s)", sunrise, formatted);
+ ret = TRUE;
+ }
+ if (ABS (self->cached_sunset - sunset) > CSD_FRAC_DAY_MAX_DELTA) {
+ self->cached_sunset = sunset;
+ g_object_notify (G_OBJECT (self), "sunset");
+ g_autofree gchar *formatted = csd_night_light_time_string_from_frac (sunset);
+ g_debug ("Sunset updated: %.3f (%s)", sunset, formatted);
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static void
+csd_night_light_set_temperature_internal (CsdNightLight *self, gdouble temperature)
+{
+ if (ABS (self->cached_temperature - temperature) <= CSD_TEMPERATURE_MAX_DELTA)
+ return;
+ self->cached_temperature = temperature;
+ g_object_notify (G_OBJECT (self), "temperature");
+}
+
+static gboolean
+csd_night_light_smooth_cb (gpointer user_data)
+{
+ CsdNightLight *self = CSD_NIGHT_LIGHT (user_data);
+ gdouble tmp;
+ gdouble frac;
+
+ /* find fraction */
+ frac = g_timer_elapsed (self->smooth_timer, NULL) / CSD_NIGHT_LIGHT_SMOOTH_SMEAR;
+ if (frac >= 1.f) {
+ csd_night_light_set_temperature_internal (self,
+ self->smooth_target_temperature);
+ self->smooth_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ /* set new temperature step using log curve */
+ tmp = self->smooth_target_temperature - self->cached_temperature;
+ tmp *= frac;
+ tmp += self->cached_temperature;
+ csd_night_light_set_temperature_internal (self, tmp);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+poll_smooth_create (CsdNightLight *self, gdouble temperature)
+{
+ g_assert (self->smooth_id == 0);
+ self->smooth_target_temperature = temperature;
+ self->smooth_timer = g_timer_new ();
+ self->smooth_id = g_timeout_add (50, csd_night_light_smooth_cb, self);
+}
+
+static void
+csd_night_light_set_temperature (CsdNightLight *self, gdouble temperature)
+{
+ /* immediate */
+ if (!self->smooth_enabled) {
+ csd_night_light_set_temperature_internal (self, temperature);
+ return;
+ }
+
+ /* Destroy any smooth transition, it will be recreated if neccessary */
+ poll_smooth_destroy (self);
+
+ /* small jump */
+ if (ABS (temperature - self->cached_temperature) < CSD_TEMPERATURE_MAX_DELTA) {
+ csd_night_light_set_temperature_internal (self, temperature);
+ return;
+ }
+
+ /* smooth out the transition */
+ poll_smooth_create (self, temperature);
+}
+
+static void
+csd_night_light_set_active (CsdNightLight *self, gboolean active)
+{
+ if (self->cached_active == active)
+ return;
+ self->cached_active = active;
+
+ /* ensure set to unity temperature */
+ if (!active)
+ csd_night_light_set_temperature (self, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+ g_object_notify (G_OBJECT (self), "active");
+}
+
+static void
+night_light_recheck (CsdNightLight *self)
+{
+ gdouble frac_day;
+ gdouble schedule_from = -1.f;
+ gdouble schedule_to = -1.f;
+ gdouble smear = CSD_NIGHT_LIGHT_POLL_SMEAR; /* hours */
+ guint temperature;
+ guint temp_smeared;
+ g_autoptr(GDateTime) dt_now = csd_night_light_get_date_time_now (self);
+
+ /* Forced mode, just set the temperature to night light.
+ * Proper rechecking will happen once forced mode is disabled again */
+ if (self->forced) {
+ temperature = g_settings_get_uint (self->settings, "night-light-temperature");
+ csd_night_light_set_temperature (self, temperature);
+ return;
+ }
+
+ /* enabled */
+ if (!g_settings_get_boolean (self->settings, "night-light-enabled")) {
+ g_debug ("night light disabled, resetting");
+ csd_night_light_set_active (self, FALSE);
+ return;
+ }
+
+ /* calculate the position of the sun */
+ if (g_settings_get_boolean (self->settings, "night-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,
+ "night-light-schedule-from");
+ schedule_to = g_settings_get_double (self->settings,
+ "night-light-schedule-to");
+ }
+
+ /* get the current hour of a day as a fraction */
+ frac_day = csd_night_light_frac_day_from_dt (dt_now);
+ g_debug ("fractional day = %.3f, limits = %.3f->%.3f",
+ frac_day, schedule_from, schedule_to);
+
+ /* disabled until tomorrow */
+ if (self->disabled_until_tmw) {
+ GTimeSpan time_span;
+ gboolean reset = FALSE;
+
+ time_span = g_date_time_difference (dt_now, self->disabled_until_tmw_dt);
+
+ /* Reset if disabled until tomorrow is more than 24h ago. */
+ if (time_span > (GTimeSpan) 24 * 60 * 60 * 1000000) {
+ g_debug ("night light disabled until tomorrow is older than 24h, resetting disabled until tomorrow");
+ reset = TRUE;
+ } else if (time_span > 0) {
+ /* Or if a sunrise lies between the time it was disabled and now. */
+ gdouble frac_disabled;
+ frac_disabled = csd_night_light_frac_day_from_dt (self->disabled_until_tmw_dt);
+ if (frac_disabled != frac_day &&
+ csd_night_light_frac_day_is_between (schedule_to,
+ frac_disabled,
+ frac_day)) {
+ g_debug ("night light sun rise happened, resetting disabled until tomorrow");
+ reset = TRUE;
+ }
+ }
+
+ if (reset) {
+ self->disabled_until_tmw = FALSE;
+ g_clear_pointer(&self->disabled_until_tmw_dt, g_date_time_unref);
+ g_object_notify (G_OBJECT (self), "disabled-until-tmw");
+ } else {
+ g_debug ("night light still day-disabled, resetting");
+ csd_night_light_set_temperature (self,
+ CSD_COLOR_TEMPERATURE_DEFAULT);
+ return;
+ }
+ }
+
+ /* lower smearing period to be smaller than the time between start/stop */
+ smear = MIN (smear,
+ MIN ( ABS (schedule_to - schedule_from),
+ 24 - ABS (schedule_to - schedule_from)));
+
+ if (!csd_night_light_frac_day_is_between (frac_day,
+ schedule_from - smear,
+ schedule_to)) {
+ g_debug ("not time for night-light");
+ csd_night_light_set_active (self, FALSE);
+ 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, "night-light-temperature");
+ if (smear < 0.01) {
+ /* Don't try to smear for extremely short or zero periods */
+ temp_smeared = temperature;
+ } else if (csd_night_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 (CSD_COLOR_TEMPERATURE_DEFAULT,
+ temperature, factor);
+ } else if (csd_night_light_frac_day_is_between (frac_day,
+ schedule_to - smear,
+ schedule_to)) {
+ gdouble factor = (frac_day - (schedule_to - smear)) / smear;
+ temp_smeared = linear_interpolate (CSD_COLOR_TEMPERATURE_DEFAULT,
+ temperature, factor);
+ } else {
+ temp_smeared = temperature;
+ }
+ g_debug ("night light mode on, using temperature of %uK (aiming for %uK)",
+ temp_smeared, temperature);
+ csd_night_light_set_active (self, TRUE);
+ csd_night_light_set_temperature (self, temp_smeared);
+}
+
+/* called when the time may have changed */
+static gboolean
+night_light_recheck_cb (gpointer user_data)
+{
+ CsdNightLight *self = CSD_NIGHT_LIGHT (user_data);
+
+ /* recheck parameters, then reschedule a new timeout */
+ night_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 (CsdNightLight *self)
+{
+ g_autoptr(GDateTime) dt_now = NULL;
+ g_autoptr(GDateTime) dt_expiry = NULL;
+
+ if (self->source != NULL)
+ return;
+
+ dt_now = csd_night_light_get_date_time_now (self);
+ dt_expiry = g_date_time_add_seconds (dt_now, CSD_NIGHT_LIGHT_POLL_TIMEOUT);
+ self->source = _gnome_datetime_source_new (dt_now,
+ dt_expiry,
+ TRUE);
+ g_source_set_callback (self->source,
+ night_light_recheck_cb,
+ self, NULL);
+ g_source_attach (self->source, NULL);
+}
+
+static void
+poll_timeout_destroy (CsdNightLight *self)
+{
+
+ if (self->source == NULL)
+ return;
+
+ g_source_destroy (self->source);
+ g_source_unref (self->source);
+ self->source = NULL;
+}
+
+static void
+settings_changed_cb (GSettings *settings, gchar *key, gpointer user_data)
+{
+ CsdNightLight *self = CSD_NIGHT_LIGHT (user_data);
+ g_debug ("settings changed");
+ night_light_recheck (self);
+}
+
+static void
+update_location_from_timezone (CsdNightLight *self)
+{
+ GTimeZone *tz = g_time_zone_new_local ();
+ const gchar *id = g_time_zone_get_identifier (tz);
+
+ for (int i = 0; i < G_N_ELEMENTS (tz_coord_list); i++)
+ {
+ const TZCoords coords = tz_coord_list[i];
+ if (g_strcmp0 (coords.timezone, id) == 0)
+ {
+ g_debug ("Coordinates updated, timezone: %s, lat:%.3f, long:%.3f.",
+ id, coords.latitude, coords.longitude);
+ g_settings_set_value (self->settings,
+ "night-light-last-coordinates",
+ g_variant_new ("(dd)", coords.latitude, coords.longitude));
+ break;
+ }
+ }
+
+ g_time_zone_unref (tz);
+}
+
+void
+csd_night_light_set_disabled_until_tmw (CsdNightLight *self, gboolean value)
+{
+ g_autoptr(GDateTime) dt = csd_night_light_get_date_time_now (self);
+
+ if (self->disabled_until_tmw == value)
+ return;
+
+ self->disabled_until_tmw = value;
+ g_clear_pointer (&self->disabled_until_tmw_dt, g_date_time_unref);
+ if (self->disabled_until_tmw)
+ self->disabled_until_tmw_dt = g_steal_pointer (&dt);
+ night_light_recheck (self);
+ g_object_notify (G_OBJECT (self), "disabled-until-tmw");
+}
+
+gboolean
+csd_night_light_get_disabled_until_tmw (CsdNightLight *self)
+{
+ return self->disabled_until_tmw;
+}
+
+void
+csd_night_light_set_forced (CsdNightLight *self, gboolean value)
+{
+ if (self->forced == value)
+ return;
+
+ self->forced = value;
+ g_object_notify (G_OBJECT (self), "forced");
+
+ /* A simple recheck might not reset the temperature if
+ * night light is currently disabled. */
+ if (!self->forced && !self->cached_active)
+ csd_night_light_set_temperature (self, CSD_COLOR_TEMPERATURE_DEFAULT);
+
+ night_light_recheck (self);
+}
+
+gboolean
+csd_night_light_get_forced (CsdNightLight *self)
+{
+ return self->forced;
+}
+
+gboolean
+csd_night_light_get_active (CsdNightLight *self)
+{
+ return self->cached_active;
+}
+
+gdouble
+csd_night_light_get_sunrise (CsdNightLight *self)
+{
+ return self->cached_sunrise;
+}
+
+gdouble
+csd_night_light_get_sunset (CsdNightLight *self)
+{
+ return self->cached_sunset;
+}
+
+gdouble
+csd_night_light_get_temperature (CsdNightLight *self)
+{
+ return self->cached_temperature;
+}
+
+gboolean
+csd_night_light_start (CsdNightLight *self, GError **error)
+{
+ night_light_recheck (self);
+ poll_timeout_create (self);
+
+ /* care about changes */
+ g_signal_connect (self->settings, "changed",
+ G_CALLBACK (settings_changed_cb), self);
+
+ update_location_from_timezone (self);
+
+ return TRUE;
+}
+
+static void
+csd_night_light_finalize (GObject *object)
+{
+ CsdNightLight *self = CSD_NIGHT_LIGHT (object);
+
+ poll_timeout_destroy (self);
+ poll_smooth_destroy (self);
+
+ g_clear_object (&self->settings);
+ g_clear_pointer (&self->datetime_override, g_date_time_unref);
+ g_clear_pointer (&self->disabled_until_tmw_dt, g_date_time_unref);
+
+ if (self->validate_id > 0) {
+ g_source_remove (self->validate_id);
+ self->validate_id = 0;
+ }
+
+ G_OBJECT_CLASS (csd_night_light_parent_class)->finalize (object);
+}
+
+static void
+csd_night_light_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CsdNightLight *self = CSD_NIGHT_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:
+ csd_night_light_set_disabled_until_tmw (self, g_value_get_boolean (value));
+ break;
+ case PROP_FORCED:
+ csd_night_light_set_forced (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+csd_night_light_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CsdNightLight *self = CSD_NIGHT_LIGHT (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->cached_active);
+ break;
+ 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, csd_night_light_get_disabled_until_tmw (self));
+ break;
+ case PROP_FORCED:
+ g_value_set_boolean (value, csd_night_light_get_forced (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+csd_night_light_class_init (CsdNightLightClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = csd_night_light_finalize;
+
+ object_class->set_property = csd_night_light_set_property;
+ object_class->get_property = csd_night_light_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ "Active",
+ "If night light functionality is active right now",
+ FALSE,
+ G_PARAM_READABLE));
+
+ 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",
+ CSD_COLOR_TEMPERATURE_MIN,
+ CSD_COLOR_TEMPERATURE_MAX,
+ CSD_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 night light is disabled until the next day",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_FORCED,
+ g_param_spec_boolean ("forced",
+ "Forced",
+ "Whether night light should be forced on, useful for previewing",
+ FALSE,
+ G_PARAM_READWRITE));
+
+}
+
+static void
+csd_night_light_init (CsdNightLight *self)
+{
+ self->smooth_enabled = TRUE;
+ self->cached_sunrise = -1.f;
+ self->cached_sunset = -1.f;
+ self->cached_temperature = CSD_COLOR_TEMPERATURE_DEFAULT;
+ self->settings = g_settings_new ("org.cinnamon.settings-daemon.plugins.color");
+}
+
+CsdNightLight *
+csd_night_light_new (void)
+{
+ return g_object_new (CSD_TYPE_NIGHT_LIGHT, NULL);
+}
diff --git a/plugins/color/csd-night-light.h b/plugins/color/csd-night-light.h
new file mode 100644
index 00000000..0786d1aa
--- /dev/null
+++ b/plugins/color/csd-night-light.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 Richard Hughes
+ *
+ * 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 __CSD_NIGHT_LIGHT_H__
+#define __CSD_NIGHT_LIGHT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CSD_TYPE_NIGHT_LIGHT (csd_night_light_get_type ())
+G_DECLARE_FINAL_TYPE (CsdNightLight, csd_night_light, CSD, NIGHT_LIGHT, GObject)
+
+CsdNightLight *csd_night_light_new (void);
+gboolean csd_night_light_start (CsdNightLight *self,
+ GError **error);
+
+gboolean csd_night_light_get_active (CsdNightLight *self);
+gdouble csd_night_light_get_sunrise (CsdNightLight *self);
+gdouble csd_night_light_get_sunset (CsdNightLight *self);
+gdouble csd_night_light_get_temperature (CsdNightLight *self);
+
+gboolean csd_night_light_get_disabled_until_tmw (CsdNightLight *self);
+void csd_night_light_set_disabled_until_tmw (CsdNightLight *self,
+ gboolean value);
+
+gboolean csd_night_light_get_forced (CsdNightLight *self);
+void csd_night_light_set_forced (CsdNightLight *self,
+ gboolean value);
+
+void csd_night_light_set_date_time_now (CsdNightLight *self,
+ GDateTime *datetime);
+void csd_night_light_set_smooth_enabled (CsdNightLight *self,
+ gboolean smooth_enabled);
+
+G_END_DECLS
+
+#endif
diff --git a/plugins/color/gcm-dmi.c b/plugins/color/gcm-dmi.c
deleted file mode 100644
index 9177f89f..00000000
--- a/plugins/color/gcm-dmi.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2009-2011 Richard Hughes
- *
- * 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
-#include
-#include
-#include
-#include
-
-#include "gcm-dmi.h"
-
-static void gcm_dmi_finalize (GObject *object);
-
-#define GCM_DMI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DMI, GcmDmiPrivate))
-
-struct _GcmDmiPrivate
-{
- gchar *name;
- gchar *version;
- gchar *vendor;
-};
-
-static gpointer gcm_dmi_object = NULL;
-
-G_DEFINE_TYPE (GcmDmi, gcm_dmi, G_TYPE_OBJECT)
-
-static gchar *
-gcm_dmi_get_from_filename (const gchar *filename)
-{
- gboolean ret;
- GError *error = NULL;
- gchar *data = NULL;
-
- /* get the contents */
- ret = g_file_get_contents (filename, &data, NULL, &error);
- if (!ret) {
- if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
- g_warning ("failed to get contents of %s: %s", filename, error->message);
- g_error_free (error);
- }
-
- /* process the random chars and trailing spaces */
- if (data != NULL) {
- g_strdelimit (data, "\t_", ' ');
- g_strdelimit (data, "\n\r", '\0');
- g_strchomp (data);
- }
-
- /* don't return an empty string */
- if (data != NULL && data[0] == '\0') {
- g_free (data);
- data = NULL;
- }
-
- return data;
-}
-
-static gchar *
-gcm_dmi_get_from_filenames (const gchar * const * filenames)
-{
- guint i;
- gchar *tmp = NULL;
-
- /* try each one in preference order */
- for (i = 0; filenames[i] != NULL; i++) {
- tmp = gcm_dmi_get_from_filename (filenames[i]);
- if (tmp != NULL)
- break;
- }
- return tmp;
-}
-
-const gchar *
-gcm_dmi_get_name (GcmDmi *dmi)
-{
- g_return_val_if_fail (GCM_IS_DMI (dmi), NULL);
- return dmi->priv->name;
-}
-
-const gchar *
-gcm_dmi_get_version (GcmDmi *dmi)
-{
- g_return_val_if_fail (GCM_IS_DMI (dmi), NULL);
- return dmi->priv->version;
-}
-
-const gchar *
-gcm_dmi_get_vendor (GcmDmi *dmi)
-{
- g_return_val_if_fail (GCM_IS_DMI (dmi), NULL);
- return dmi->priv->vendor;
-}
-
-static void
-gcm_dmi_class_init (GcmDmiClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = gcm_dmi_finalize;
- g_type_class_add_private (klass, sizeof (GcmDmiPrivate));
-}
-
-static void
-gcm_dmi_init (GcmDmi *dmi)
-{
-#if defined(__linux__)
- const gchar *sysfs_name[] = {
- "/sys/class/dmi/id/product_name",
- "/sys/class/dmi/id/board_name",
- NULL};
- const gchar *sysfs_version[] = {
- "/sys/class/dmi/id/product_version",
- "/sys/class/dmi/id/chassis_version",
- "/sys/class/dmi/id/board_version",
- NULL};
- const gchar *sysfs_vendor[] = {
- "/sys/class/dmi/id/sys_vendor",
- "/sys/class/dmi/id/chassis_vendor",
- "/sys/class/dmi/id/board_vendor",
- NULL};
-#else
-#warning Please add dmi support for your OS
- const gchar *sysfs_name[] = { NULL };
- const gchar *sysfs_version[] = { NULL };
- const gchar *sysfs_vendor[] = { NULL };
-#endif
-
- dmi->priv = GCM_DMI_GET_PRIVATE (dmi);
-
- /* get all the possible data now */
- dmi->priv->name = gcm_dmi_get_from_filenames (sysfs_name);
- dmi->priv->version = gcm_dmi_get_from_filenames (sysfs_version);
- dmi->priv->vendor = gcm_dmi_get_from_filenames (sysfs_vendor);
-}
-
-static void
-gcm_dmi_finalize (GObject *object)
-{
- GcmDmi *dmi = GCM_DMI (object);
-
- g_free (dmi->priv->name);
- g_free (dmi->priv->version);
- g_free (dmi->priv->vendor);
-
- G_OBJECT_CLASS (gcm_dmi_parent_class)->finalize (object);
-}
-
-GcmDmi *
-gcm_dmi_new (void)
-{
- if (gcm_dmi_object != NULL) {
- g_object_ref (gcm_dmi_object);
- } else {
- gcm_dmi_object = g_object_new (GCM_TYPE_DMI, NULL);
- g_object_add_weak_pointer (gcm_dmi_object, &gcm_dmi_object);
- }
- return GCM_DMI (gcm_dmi_object);
-}
diff --git a/plugins/color/gcm-dmi.h b/plugins/color/gcm-dmi.h
deleted file mode 100644
index 5aeb5b72..00000000
--- a/plugins/color/gcm-dmi.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2009-2010 Richard Hughes
- *
- * 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 __GCM_DMI_H
-#define __GCM_DMI_H
-
-#include
-
-G_BEGIN_DECLS
-
-#define GCM_TYPE_DMI (gcm_dmi_get_type ())
-#define GCM_DMI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DMI, GcmDmi))
-#define GCM_DMI_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DMI, GcmDmiClass))
-#define GCM_IS_DMI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DMI))
-#define GCM_IS_DMI_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DMI))
-#define GCM_DMI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DMI, GcmDmiClass))
-
-typedef struct _GcmDmiPrivate GcmDmiPrivate;
-typedef struct _GcmDmi GcmDmi;
-typedef struct _GcmDmiClass GcmDmiClass;
-
-struct _GcmDmi
-{
- GObject parent;
- GcmDmiPrivate *priv;
-};
-
-struct _GcmDmiClass
-{
- GObjectClass parent_class;
-};
-
-GType gcm_dmi_get_type (void);
-GcmDmi *gcm_dmi_new (void);
-const gchar *gcm_dmi_get_name (GcmDmi *dmi);
-const gchar *gcm_dmi_get_version (GcmDmi *dmi);
-const gchar *gcm_dmi_get_vendor (GcmDmi *dmi);
-
-G_END_DECLS
-
-#endif /* __GCM_DMI_H */
-
diff --git a/plugins/color/gcm-edid.c b/plugins/color/gcm-edid.c
deleted file mode 100644
index 2e9564d3..00000000
--- a/plugins/color/gcm-edid.c
+++ /dev/null
@@ -1,457 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 Soren Sandmann
- * Copyright (C) 2009-2011 Richard Hughes
- *
- * 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
-#include
-#include
-#include
-#include
-#include
-
-#include "gcm-edid.h"
-
-static void gcm_edid_finalize (GObject *object);
-
-#define GCM_EDID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_EDID, GcmEdidPrivate))
-
-struct _GcmEdidPrivate
-{
- gchar *monitor_name;
- gchar *vendor_name;
- gchar *serial_number;
- gchar *eisa_id;
- gchar *checksum;
- gchar *pnp_id;
- guint width;
- guint height;
- gfloat gamma;
- CdColorYxy *red;
- CdColorYxy *green;
- CdColorYxy *blue;
- CdColorYxy *white;
- GnomePnpIds *pnp_ids;
-};
-
-G_DEFINE_TYPE (GcmEdid, gcm_edid, G_TYPE_OBJECT)
-
-#define GCM_EDID_OFFSET_PNPID 0x08
-#define GCM_EDID_OFFSET_SERIAL 0x0c
-#define GCM_EDID_OFFSET_SIZE 0x15
-#define GCM_EDID_OFFSET_GAMMA 0x17
-#define GCM_EDID_OFFSET_DATA_BLOCKS 0x36
-#define GCM_EDID_OFFSET_LAST_BLOCK 0x6c
-#define GCM_EDID_OFFSET_EXTENSION_BLOCK_COUNT 0x7e
-
-#define GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
-#define GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
-#define GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA 0xf9
-#define GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
-#define GCM_DESCRIPTOR_COLOR_POINT 0xfb
-
-GQuark
-gcm_edid_error_quark (void)
-{
- static GQuark quark = 0;
- if (!quark)
- quark = g_quark_from_static_string ("gcm_edid_error");
- return quark;
-}
-
-const gchar *
-gcm_edid_get_monitor_name (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->monitor_name;
-}
-
-const gchar *
-gcm_edid_get_vendor_name (GcmEdid *edid)
-{
- GcmEdidPrivate *priv = edid->priv;
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
-
- if (priv->vendor_name == NULL)
- priv->vendor_name = gnome_pnp_ids_get_pnp_id (priv->pnp_ids, priv->pnp_id);
- return priv->vendor_name;
-}
-
-const gchar *
-gcm_edid_get_serial_number (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->serial_number;
-}
-
-const gchar *
-gcm_edid_get_eisa_id (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->eisa_id;
-}
-
-const gchar *
-gcm_edid_get_checksum (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->checksum;
-}
-
-const gchar *
-gcm_edid_get_pnp_id (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->pnp_id;
-}
-
-guint
-gcm_edid_get_width (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), 0);
- return edid->priv->width;
-}
-
-guint
-gcm_edid_get_height (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), 0);
- return edid->priv->height;
-}
-
-gfloat
-gcm_edid_get_gamma (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), 0.0f);
- return edid->priv->gamma;
-}
-
-const CdColorYxy *
-gcm_edid_get_red (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->red;
-}
-
-const CdColorYxy *
-gcm_edid_get_green (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->green;
-}
-
-const CdColorYxy *
-gcm_edid_get_blue (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->blue;
-}
-
-const CdColorYxy *
-gcm_edid_get_white (GcmEdid *edid)
-{
- g_return_val_if_fail (GCM_IS_EDID (edid), NULL);
- return edid->priv->white;
-}
-
-void
-gcm_edid_reset (GcmEdid *edid)
-{
- GcmEdidPrivate *priv = edid->priv;
-
- g_return_if_fail (GCM_IS_EDID (edid));
-
- /* free old data */
- g_free (priv->monitor_name);
- g_free (priv->vendor_name);
- g_free (priv->serial_number);
- g_free (priv->eisa_id);
- g_free (priv->checksum);
-
- /* do not deallocate, just blank */
- priv->pnp_id[0] = '\0';
-
- /* set to default values */
- priv->monitor_name = NULL;
- priv->vendor_name = NULL;
- priv->serial_number = NULL;
- priv->eisa_id = NULL;
- priv->checksum = NULL;
- priv->width = 0;
- priv->height = 0;
- priv->gamma = 0.0f;
-}
-
-static gint
-gcm_edid_get_bit (gint in, gint bit)
-{
- return (in & (1 << bit)) >> bit;
-}
-
-/**
- * gcm_edid_get_bits:
- **/
-static gint
-gcm_edid_get_bits (gint in, gint begin, gint end)
-{
- gint mask = (1 << (end - begin + 1)) - 1;
-
- return (in >> begin) & mask;
-}
-
-/**
- * gcm_edid_decode_fraction:
- **/
-static gdouble
-gcm_edid_decode_fraction (gint high, gint low)
-{
- gdouble result = 0.0;
- gint i;
-
- high = (high << 2) | low;
- for (i = 0; i < 10; ++i)
- result += gcm_edid_get_bit (high, i) * pow (2, i - 10);
- return result;
-}
-
-static gchar *
-gcm_edid_parse_string (const guint8 *data)
-{
- gchar *text;
- guint i;
- guint replaced = 0;
-
- /* this is always 13 bytes, but we can't guarantee it's null
- * terminated or not junk. */
- text = g_strndup ((const gchar *) data, 13);
-
- /* remove insane newline chars */
- g_strdelimit (text, "\n\r", '\0');
-
- /* remove spaces */
- g_strchomp (text);
-
- /* nothing left? */
- if (text[0] == '\0') {
- g_free (text);
- text = NULL;
- goto out;
- }
-
- /* ensure string is printable */
- for (i = 0; text[i] != '\0'; i++) {
- if (!g_ascii_isprint (text[i])) {
- text[i] = '-';
- replaced++;
- }
- }
-
- /* if the string is junk, ignore the string */
- if (replaced > 4) {
- g_free (text);
- text = NULL;
- goto out;
- }
-out:
- return text;
-}
-
-gboolean
-gcm_edid_parse (GcmEdid *edid, const guint8 *data, gsize length, GError **error)
-{
- gboolean ret = TRUE;
- guint i;
- GcmEdidPrivate *priv = edid->priv;
- guint32 serial;
- gchar *tmp;
-
- /* check header */
- if (length < 128) {
- g_set_error_literal (error,
- GCM_EDID_ERROR,
- GCM_EDID_ERROR_FAILED_TO_PARSE,
- "EDID length is too small");
- ret = FALSE;
- goto out;
- }
- if (data[0] != 0x00 || data[1] != 0xff) {
- g_set_error_literal (error,
- GCM_EDID_ERROR,
- GCM_EDID_ERROR_FAILED_TO_PARSE,
- "Failed to parse EDID header");
- ret = FALSE;
- goto out;
- }
-
- /* free old data */
- gcm_edid_reset (edid);
-
- /* decode the PNP ID from three 5 bit words packed into 2 bytes
- * /--08--\/--09--\
- * 7654321076543210
- * |\---/\---/\---/
- * R C1 C2 C3 */
- priv->pnp_id[0] = 'A' + ((data[GCM_EDID_OFFSET_PNPID+0] & 0x7c) / 4) - 1;
- priv->pnp_id[1] = 'A' + ((data[GCM_EDID_OFFSET_PNPID+0] & 0x3) * 8) + ((data[GCM_EDID_OFFSET_PNPID+1] & 0xe0) / 32) - 1;
- priv->pnp_id[2] = 'A' + (data[GCM_EDID_OFFSET_PNPID+1] & 0x1f) - 1;
-
- /* maybe there isn't a ASCII serial number descriptor, so use this instead */
- serial = (guint32) data[GCM_EDID_OFFSET_SERIAL+0];
- serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+1] * 0x100;
- serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+2] * 0x10000;
- serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+3] * 0x1000000;
- if (serial > 0)
- priv->serial_number = g_strdup_printf ("%" G_GUINT32_FORMAT, serial);
-
- /* get the size */
- priv->width = data[GCM_EDID_OFFSET_SIZE+0];
- priv->height = data[GCM_EDID_OFFSET_SIZE+1];
-
- /* we don't care about aspect */
- if (priv->width == 0 || priv->height == 0) {
- priv->width = 0;
- priv->height = 0;
- }
-
- /* get gamma */
- if (data[GCM_EDID_OFFSET_GAMMA] == 0xff) {
- priv->gamma = 1.0f;
- } else {
- priv->gamma = ((gfloat) data[GCM_EDID_OFFSET_GAMMA] / 100) + 1;
- }
-
- /* get color red */
- priv->red->x = gcm_edid_decode_fraction (data[0x1b], gcm_edid_get_bits (data[0x19], 6, 7));
- priv->red->y = gcm_edid_decode_fraction (data[0x1c], gcm_edid_get_bits (data[0x19], 4, 5));
-
- /* get color green */
- priv->green->x = gcm_edid_decode_fraction (data[0x1d], gcm_edid_get_bits (data[0x19], 2, 3));
- priv->green->y = gcm_edid_decode_fraction (data[0x1e], gcm_edid_get_bits (data[0x19], 0, 1));
-
- /* get color blue */
- priv->blue->x = gcm_edid_decode_fraction (data[0x1f], gcm_edid_get_bits (data[0x1a], 6, 7));
- priv->blue->y = gcm_edid_decode_fraction (data[0x20], gcm_edid_get_bits (data[0x1a], 4, 5));
-
- /* get color white */
- priv->white->x = gcm_edid_decode_fraction (data[0x21], gcm_edid_get_bits (data[0x1a], 2, 3));
- priv->white->y = gcm_edid_decode_fraction (data[0x22], gcm_edid_get_bits (data[0x1a], 0, 1));
-
- /* parse EDID data */
- for (i = GCM_EDID_OFFSET_DATA_BLOCKS;
- i <= GCM_EDID_OFFSET_LAST_BLOCK;
- i += 18) {
- /* ignore pixel clock data */
- if (data[i] != 0)
- continue;
- if (data[i+2] != 0)
- continue;
-
- /* any useful blocks? */
- if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
- tmp = gcm_edid_parse_string (&data[i+5]);
- if (tmp != NULL) {
- g_free (priv->monitor_name);
- priv->monitor_name = tmp;
- }
- } else if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
- tmp = gcm_edid_parse_string (&data[i+5]);
- if (tmp != NULL) {
- g_free (priv->serial_number);
- priv->serial_number = tmp;
- }
- } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA) {
- g_warning ("failing to parse color management data");
- } else if (data[i+3] == GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
- tmp = gcm_edid_parse_string (&data[i+5]);
- if (tmp != NULL) {
- g_free (priv->eisa_id);
- priv->eisa_id = tmp;
- }
- } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_POINT) {
- if (data[i+3+9] != 0xff) {
- /* extended EDID block(1) which contains
- * a better gamma value */
- priv->gamma = ((gfloat) data[i+3+9] / 100) + 1;
- }
- if (data[i+3+14] != 0xff) {
- /* extended EDID block(2) which contains
- * a better gamma value */
- priv->gamma = ((gfloat) data[i+3+9] / 100) + 1;
- }
- }
- }
-
- /* calculate checksum */
- priv->checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, length);
-out:
- return ret;
-}
-
-static void
-gcm_edid_class_init (GcmEdidClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = gcm_edid_finalize;
- g_type_class_add_private (klass, sizeof (GcmEdidPrivate));
-}
-
-static void
-gcm_edid_init (GcmEdid *edid)
-{
- edid->priv = GCM_EDID_GET_PRIVATE (edid);
- edid->priv->pnp_ids = gnome_pnp_ids_new ();
- edid->priv->pnp_id = g_new0 (gchar, 4);
- edid->priv->red = cd_color_yxy_new ();
- edid->priv->green = cd_color_yxy_new ();
- edid->priv->blue = cd_color_yxy_new ();
- edid->priv->white = cd_color_yxy_new ();
-}
-
-static void
-gcm_edid_finalize (GObject *object)
-{
- GcmEdid *edid = GCM_EDID (object);
- GcmEdidPrivate *priv = edid->priv;
-
- g_free (priv->monitor_name);
- g_free (priv->vendor_name);
- g_free (priv->serial_number);
- g_free (priv->eisa_id);
- g_free (priv->checksum);
- g_free (priv->pnp_id);
- cd_color_yxy_free (priv->white);
- cd_color_yxy_free (priv->red);
- cd_color_yxy_free (priv->green);
- cd_color_yxy_free (priv->blue);
- g_object_unref (priv->pnp_ids);
-
- G_OBJECT_CLASS (gcm_edid_parent_class)->finalize (object);
-}
-
-GcmEdid *
-gcm_edid_new (void)
-{
- GcmEdid *edid;
- edid = g_object_new (GCM_TYPE_EDID, NULL);
- return GCM_EDID (edid);
-}
-
diff --git a/plugins/color/gcm-edid.h b/plugins/color/gcm-edid.h
deleted file mode 100644
index 5eff73c9..00000000
--- a/plugins/color/gcm-edid.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2009-2010 Richard Hughes
- *
- * 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 __GCM_EDID_H
-#define __GCM_EDID_H
-
-#include
-#include
-
-G_BEGIN_DECLS
-
-#define GCM_TYPE_EDID (gcm_edid_get_type ())
-#define GCM_EDID(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_EDID, GcmEdid))
-#define GCM_EDID_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_EDID, GcmEdidClass))
-#define GCM_IS_EDID(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_EDID))
-#define GCM_IS_EDID_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_EDID))
-#define GCM_EDID_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_EDID, GcmEdidClass))
-#define GCM_EDID_ERROR (gcm_edid_error_quark ())
-
-typedef struct _GcmEdidPrivate GcmEdidPrivate;
-typedef struct _GcmEdid GcmEdid;
-typedef struct _GcmEdidClass GcmEdidClass;
-
-struct _GcmEdid
-{
- GObject parent;
- GcmEdidPrivate *priv;
-};
-
-struct _GcmEdidClass
-{
- GObjectClass parent_class;
-};
-
-enum
-{
- GCM_EDID_ERROR_FAILED_TO_PARSE
-};
-
-GType gcm_edid_get_type (void);
-GQuark gcm_edid_error_quark (void);
-GcmEdid *gcm_edid_new (void);
-void gcm_edid_reset (GcmEdid *edid);
-gboolean gcm_edid_parse (GcmEdid *edid,
- const guint8 *data,
- gsize length,
- GError **error);
-const gchar *gcm_edid_get_monitor_name (GcmEdid *edid);
-const gchar *gcm_edid_get_vendor_name (GcmEdid *edid);
-const gchar *gcm_edid_get_serial_number (GcmEdid *edid);
-const gchar *gcm_edid_get_eisa_id (GcmEdid *edid);
-const gchar *gcm_edid_get_checksum (GcmEdid *edid);
-const gchar *gcm_edid_get_pnp_id (GcmEdid *edid);
-guint gcm_edid_get_width (GcmEdid *edid);
-guint gcm_edid_get_height (GcmEdid *edid);
-gfloat gcm_edid_get_gamma (GcmEdid *edid);
-const CdColorYxy *gcm_edid_get_red (GcmEdid *edid);
-const CdColorYxy *gcm_edid_get_green (GcmEdid *edid);
-const CdColorYxy *gcm_edid_get_blue (GcmEdid *edid);
-const CdColorYxy *gcm_edid_get_white (GcmEdid *edid);
-
-G_END_DECLS
-
-#endif /* __GCM_EDID_H */
-
diff --git a/plugins/color/gcm-profile-store.c b/plugins/color/gcm-profile-store.c
deleted file mode 100644
index ee819253..00000000
--- a/plugins/color/gcm-profile-store.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2009-2011 Richard Hughes
- *
- * 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
-#include
-
-#include "gcm-profile-store.h"
-
-static void gcm_profile_store_finalize (GObject *object);
-
-#define GCM_PROFILE_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStorePrivate))
-
-struct _GcmProfileStorePrivate
-{
- GPtrArray *filename_array;
- GPtrArray *directory_array;
- GCancellable *cancellable;
-};
-
-enum {
- SIGNAL_ADDED,
- SIGNAL_REMOVED,
- SIGNAL_CHANGED,
- SIGNAL_LAST
-};
-
-static guint signals[SIGNAL_LAST] = { 0 };
-
-G_DEFINE_TYPE (GcmProfileStore, gcm_profile_store, G_TYPE_OBJECT)
-
-static void gcm_profile_store_search_path (GcmProfileStore *profile_store, const gchar *path, guint depth);
-static void gcm_profile_store_process_child (GcmProfileStore *profile_store, const gchar *path, GFileInfo *info);
-
-#define GCM_PROFILE_STORE_MAX_RECURSION_LEVELS 2
-
-typedef struct {
- gchar *path;
- GFileMonitor *monitor;
- guint depth;
-} GcmProfileStoreDirHelper;
-
-static void
-gcm_profile_store_helper_free (GcmProfileStoreDirHelper *helper)
-{
- g_free (helper->path);
- if (helper->monitor != NULL)
- g_object_unref (helper->monitor);
- g_free (helper);
-}
-
-static const gchar *
-gcm_profile_store_find_filename (GcmProfileStore *profile_store, const gchar *filename)
-{
- const gchar *tmp;
- guint i;
- GPtrArray *array = profile_store->priv->filename_array;
-
- for (i=0; ilen; i++) {
- tmp = g_ptr_array_index (array, i);
- if (g_strcmp0 (filename, tmp) == 0)
- return tmp;
- }
- return NULL;
-}
-
-static GcmProfileStoreDirHelper *
-gcm_profile_store_find_directory (GcmProfileStore *profile_store, const gchar *path)
-{
- GcmProfileStoreDirHelper *tmp;
- guint i;
- GPtrArray *array = profile_store->priv->directory_array;
-
- for (i=0; ilen; i++) {
- tmp = g_ptr_array_index (array, i);
- if (g_strcmp0 (path, tmp->path) == 0)
- return tmp;
- }
- return NULL;
-}
-
-static gboolean
-gcm_profile_store_remove_profile (GcmProfileStore *profile_store,
- const gchar *filename)
-{
- gboolean ret = FALSE;
- const gchar *tmp;
- gchar *filename_dup = NULL;
-
- GcmProfileStorePrivate *priv = profile_store->priv;
-
- /* find exact pointer */
- tmp = gcm_profile_store_find_filename (profile_store, filename);
- if (tmp == NULL)
- goto out;
-
- /* dup so we can emit the signal */
- filename_dup = g_strdup (tmp);
- ret = g_ptr_array_remove (priv->filename_array, (gpointer)tmp);
- if (!ret) {
- g_warning ("failed to remove %s", filename);
- goto out;
- }
-
- /* emit a signal */
- g_debug ("emit removed: %s", filename_dup);
- g_signal_emit (profile_store, signals[SIGNAL_REMOVED], 0, filename_dup);
-out:
- g_free (filename_dup);
- return ret;
-}
-
-static void
-gcm_profile_store_add_profile (GcmProfileStore *profile_store, const gchar *filename)
-{
- GcmProfileStorePrivate *priv = profile_store->priv;
-
- /* add to list */
- g_ptr_array_add (priv->filename_array, g_strdup (filename));
-
- /* emit a signal */
- g_debug ("emit add: %s", filename);
- g_signal_emit (profile_store, signals[SIGNAL_ADDED], 0, filename);
-}
-
-static void
-gcm_profile_store_created_query_info_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GFileInfo *info;
- GError *error = NULL;
- gchar *path;
- GFile *file = G_FILE (source_object);
- GFile *parent;
- GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data);
-
- info = g_file_query_info_finish (file, res, &error);
- if (info == NULL) {
- g_warning ("failed to get info about deleted file: %s",
- error->message);
- g_error_free (error);
- return;
- }
- parent = g_file_get_parent (file);
- path = g_file_get_path (parent);
- gcm_profile_store_process_child (profile_store,
- path,
- info);
- g_free (path);
- g_object_unref (info);
- g_object_unref (parent);
-}
-
-static void
-gcm_profile_store_remove_from_prefix (GcmProfileStore *profile_store,
- const gchar *prefix)
-{
- guint i;
- const gchar *path;
- GcmProfileStorePrivate *priv = profile_store->priv;
-
- for (i = 0; i < priv->filename_array->len; i++) {
- path = g_ptr_array_index (priv->filename_array, i);
- if (g_str_has_prefix (path, prefix)) {
- g_debug ("auto-removed %s as path removed", path);
- gcm_profile_store_remove_profile (profile_store, path);
- }
- }
-}
-
-static void
-gcm_profile_store_file_monitor_changed_cb (GFileMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GcmProfileStore *profile_store)
-{
- gchar *path = NULL;
- gchar *parent_path = NULL;
- const gchar *tmp;
- GcmProfileStoreDirHelper *helper;
-
- /* profile was deleted */
- if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
-
- /* we can either have two things here, a directory or a
- * file. We can't call g_file_query_info_async() as the
- * inode doesn't exist anymore */
- path = g_file_get_path (file);
- tmp = gcm_profile_store_find_filename (profile_store, path);
- if (tmp != NULL) {
- /* is a file */
- gcm_profile_store_remove_profile (profile_store, path);
- goto out;
- }
-
- /* is a directory, urgh. Remove all profiles there. */
- gcm_profile_store_remove_from_prefix (profile_store, path);
- helper = gcm_profile_store_find_directory (profile_store, path);
- if (helper != NULL) {
- g_ptr_array_remove (profile_store->priv->directory_array,
- helper);
- }
- goto out;
- }
-
- /* only care about created objects */
- if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
- g_file_query_info_async (file,
- G_FILE_ATTRIBUTE_STANDARD_NAME ","
- G_FILE_ATTRIBUTE_STANDARD_TYPE,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- G_PRIORITY_LOW,
- NULL,
- gcm_profile_store_created_query_info_cb,
- profile_store);
- goto out;
- }
-out:
- g_free (path);
- g_free (parent_path);
-}
-
-static void
-gcm_profile_store_process_child (GcmProfileStore *profile_store,
- const gchar *path,
- GFileInfo *info)
-{
- gchar *full_path = NULL;
- const gchar *name;
- GcmProfileStoreDirHelper *helper;
-
- /* check we're not in a loop */
- helper = gcm_profile_store_find_directory (profile_store, path);
- if (helper == NULL)
- goto out;
- if (helper->depth > GCM_PROFILE_STORE_MAX_RECURSION_LEVELS) {
- g_warning ("recursing more than %i levels deep is insane",
- GCM_PROFILE_STORE_MAX_RECURSION_LEVELS);
- goto out;
- }
-
- /* make the compete path */
- name = g_file_info_get_name (info);
- full_path = g_build_filename (path, name, NULL);
-
- /* if a directory */
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
- gcm_profile_store_search_path (profile_store,
- full_path,
- helper->depth + 1);
- goto out;
- }
-
- /* ignore temp files */
- if (g_strrstr (full_path, ".goutputstream") != NULL) {
- g_debug ("ignoring gvfs temporary file");
- goto out;
- }
-
- /* is a file */
- gcm_profile_store_add_profile (profile_store, full_path);
-out:
- g_free (full_path);
-}
-
-static void
-gcm_profile_store_next_files_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GList *files;
- GList *f;
- GError *error = NULL;
- GFileInfo *info;
- GFile *file;
- gchar *path;
- GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object);
- GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data);
-
- files = g_file_enumerator_next_files_finish (enumerator,
- res,
- &error);
- if (files == NULL) {
- /* special value, meaning "no more files to process" */
- return;
- }
- if (error != NULL) {
- g_warning ("failed to get data about enumerated directory: %s",
- error->message);
- g_error_free (error);
- return;
- }
-
- /* get each file */
- file = g_file_enumerator_get_container (enumerator);
- path = g_file_get_path (file);
- for (f = files; f != NULL; f = f->next) {
- info = G_FILE_INFO (f->data);
- gcm_profile_store_process_child (profile_store, path, info);
- }
-
- /* continue to get the rest of the data in chunks */
- g_file_enumerator_next_files_async (enumerator,
- 5,
- G_PRIORITY_LOW,
- profile_store->priv->cancellable,
- gcm_profile_store_next_files_cb,
- user_data);
-
- g_free (path);
- g_list_foreach (files, (GFunc) g_object_unref, NULL);
- g_list_free (files);
-}
-
-static void
-gcm_profile_store_enumerate_children_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GError *error = NULL;
- GFileEnumerator *enumerator;
- GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data);
-
- enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
- res,
- &error);
- if (enumerator == NULL) {
- GcmProfileStoreDirHelper *helper;
- gchar *path = NULL;
-
- path = g_file_get_path (G_FILE (source_object));
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- g_debug ("failed to enumerate directory %s: %s",
- path, error->message);
- else
- g_warning ("failed to enumerate directory %s: %s",
- path, error->message);
- helper = gcm_profile_store_find_directory (profile_store, path);
- if (helper)
- g_ptr_array_remove (profile_store->priv->directory_array, helper);
- g_error_free (error);
- g_free (path);
- return;
- }
-
- /* get the first chunk of data */
- g_file_enumerator_next_files_async (enumerator,
- 5,
- G_PRIORITY_LOW,
- profile_store->priv->cancellable,
- gcm_profile_store_next_files_cb,
- user_data);
- g_object_unref (enumerator);
-}
-
-static void
-gcm_profile_store_search_path (GcmProfileStore *profile_store, const gchar *path, guint depth)
-{
- GFile *file = NULL;
- GError *error = NULL;
- GcmProfileStoreDirHelper *helper;
-
- file = g_file_new_for_path (path);
-
- /* add an inotify watch if not already added */
- helper = gcm_profile_store_find_directory (profile_store, path);
- if (helper == NULL) {
- helper = g_new0 (GcmProfileStoreDirHelper, 1);
- helper->depth = depth;
- helper->path = g_strdup (path);
- helper->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error);
- if (helper->monitor == NULL) {
- g_debug ("failed to monitor path: %s", error->message);
- g_error_free (error);
- gcm_profile_store_helper_free (helper);
- goto out;
- }
- g_signal_connect (helper->monitor, "changed",
- G_CALLBACK(gcm_profile_store_file_monitor_changed_cb),
- profile_store);
- g_ptr_array_add (profile_store->priv->directory_array, helper);
- }
-
- /* get contents of directory */
- g_file_enumerate_children_async (file,
- G_FILE_ATTRIBUTE_STANDARD_NAME ","
- G_FILE_ATTRIBUTE_STANDARD_TYPE,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- G_PRIORITY_LOW,
- profile_store->priv->cancellable,
- gcm_profile_store_enumerate_children_cb,
- profile_store);
-out:
- g_object_unref (file);
-}
-
-static gboolean
-gcm_profile_store_mkdir_with_parents (const gchar *filename,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret;
- GFile *file;
-
- /* ensure destination exists */
- file = g_file_new_for_path (filename);
- ret = g_file_make_directory_with_parents (file, cancellable, error);
- g_object_unref (file);
-
- return ret;
-}
-
-gboolean
-gcm_profile_store_search (GcmProfileStore *profile_store)
-{
- gchar *path;
- gboolean ret;
- GError *error = NULL;
-
- /* get Linux per-user profiles */
- path = g_build_filename (g_get_user_data_dir (), "icc", NULL);
- ret = gcm_profile_store_mkdir_with_parents (path,
- profile_store->priv->cancellable,
- &error);
- if (!ret &&
- !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
- g_warning ("failed to create directory on startup: %s", error->message);
- } else {
- gcm_profile_store_search_path (profile_store, path, 0);
- }
- g_free (path);
- g_clear_error (&error);
-
- /* get per-user profiles from obsolete location */
- path = g_build_filename (g_get_home_dir (), ".color", "icc", NULL);
- gcm_profile_store_search_path (profile_store, path, 0);
- g_free (path);
- return TRUE;
-}
-
-static void
-gcm_profile_store_class_init (GcmProfileStoreClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = gcm_profile_store_finalize;
-
- signals[SIGNAL_ADDED] =
- g_signal_new ("added",
- G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GcmProfileStoreClass, added),
- NULL, NULL, g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
- signals[SIGNAL_REMOVED] =
- g_signal_new ("removed",
- G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GcmProfileStoreClass, removed),
- NULL, NULL, g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- g_type_class_add_private (klass, sizeof (GcmProfileStorePrivate));
-}
-
-static void
-gcm_profile_store_init (GcmProfileStore *profile_store)
-{
- profile_store->priv = GCM_PROFILE_STORE_GET_PRIVATE (profile_store);
- profile_store->priv->cancellable = g_cancellable_new ();
- profile_store->priv->filename_array = g_ptr_array_new_with_free_func (g_free);
- profile_store->priv->directory_array = g_ptr_array_new_with_free_func ((GDestroyNotify) gcm_profile_store_helper_free);
-}
-
-static void
-gcm_profile_store_finalize (GObject *object)
-{
- GcmProfileStore *profile_store = GCM_PROFILE_STORE (object);
- GcmProfileStorePrivate *priv = profile_store->priv;
-
- g_cancellable_cancel (profile_store->priv->cancellable);
- g_object_unref (profile_store->priv->cancellable);
- g_ptr_array_unref (priv->filename_array);
- g_ptr_array_unref (priv->directory_array);
-
- G_OBJECT_CLASS (gcm_profile_store_parent_class)->finalize (object);
-}
-
-GcmProfileStore *
-gcm_profile_store_new (void)
-{
- GcmProfileStore *profile_store;
- profile_store = g_object_new (GCM_TYPE_PROFILE_STORE, NULL);
- return GCM_PROFILE_STORE (profile_store);
-}
-
diff --git a/plugins/color/gcm-profile-store.h b/plugins/color/gcm-profile-store.h
deleted file mode 100644
index 095bcf01..00000000
--- a/plugins/color/gcm-profile-store.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2009-2011 Richard Hughes
- *
- * 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 __GCM_PROFILE_STORE_H
-#define __GCM_PROFILE_STORE_H
-
-#include
-
-G_BEGIN_DECLS
-
-#define GCM_TYPE_PROFILE_STORE (gcm_profile_store_get_type ())
-#define GCM_PROFILE_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStore))
-#define GCM_PROFILE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_PROFILE_STORE, GcmProfileStoreClass))
-#define GCM_IS_PROFILE_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_PROFILE_STORE))
-#define GCM_IS_PROFILE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_PROFILE_STORE))
-#define GCM_PROFILE_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStoreClass))
-
-typedef struct _GcmProfileStorePrivate GcmProfileStorePrivate;
-typedef struct _GcmProfileStore GcmProfileStore;
-typedef struct _GcmProfileStoreClass GcmProfileStoreClass;
-
-struct _GcmProfileStore
-{
- GObject parent;
- GcmProfileStorePrivate *priv;
-};
-
-struct _GcmProfileStoreClass
-{
- GObjectClass parent_class;
- void (* added) (const gchar *filename);
- void (* removed) (const gchar *filename);
-};
-
-GType gcm_profile_store_get_type (void);
-GcmProfileStore *gcm_profile_store_new (void);
-gboolean gcm_profile_store_search (GcmProfileStore *profile_store);
-
-G_END_DECLS
-
-#endif /* __GCM_PROFILE_STORE_H */
diff --git a/plugins/color/gcm-self-test.c b/plugins/color/gcm-self-test.c
deleted file mode 100644
index aa3345ec..00000000
--- a/plugins/color/gcm-self-test.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2007-2011 Richard Hughes
- *
- * 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
-#include
-#include
-
-#include "gcm-edid.h"
-#include "gcm-dmi.h"
-
-static void
-gcm_test_dmi_func (void)
-{
- GcmDmi *dmi;
-
- dmi = gcm_dmi_new ();
- g_assert (dmi != NULL);
- g_assert (gcm_dmi_get_name (dmi) != NULL);
- g_assert (gcm_dmi_get_vendor (dmi) != NULL);
- g_object_unref (dmi);
-}
-
-static void
-gcm_test_edid_func (void)
-{
- GcmEdid *edid;
- gchar *data;
- gboolean ret;
- GError *error = NULL;
- gsize length = 0;
-
- edid = gcm_edid_new ();
- g_assert (edid != NULL);
-
- /* LG 21" LCD panel */
- ret = g_file_get_contents (TESTDATADIR "/LG-L225W-External.bin",
- &data, &length, &error);
- g_assert_no_error (error);
- g_assert (ret);
- ret = gcm_edid_parse (edid, (const guint8 *) data, length, &error);
- g_assert_no_error (error);
- g_assert (ret);
-
- g_assert_cmpstr (gcm_edid_get_monitor_name (edid), ==, "L225W");
- g_assert_cmpstr (gcm_edid_get_vendor_name (edid), ==, "Goldstar Company Ltd");
- g_assert_cmpstr (gcm_edid_get_serial_number (edid), ==, "34398");
- g_assert_cmpstr (gcm_edid_get_eisa_id (edid), ==, NULL);
- g_assert_cmpstr (gcm_edid_get_checksum (edid), ==, "0bb44865bb29984a4bae620656c31368");
- g_assert_cmpstr (gcm_edid_get_pnp_id (edid), ==, "GSM");
- g_assert_cmpint (gcm_edid_get_height (edid), ==, 30);
- g_assert_cmpint (gcm_edid_get_width (edid), ==, 47);
- g_assert_cmpfloat (gcm_edid_get_gamma (edid), >=, 2.2f - 0.01);
- g_assert_cmpfloat (gcm_edid_get_gamma (edid), <, 2.2f + 0.01);
- g_free (data);
-
- /* Lenovo T61 internal Panel */
- ret = g_file_get_contents (TESTDATADIR "/Lenovo-T61-Internal.bin",
- &data, &length, &error);
- g_assert_no_error (error);
- g_assert (ret);
- ret = gcm_edid_parse (edid, (const guint8 *) data, length, &error);
- g_assert_no_error (error);
- g_assert (ret);
-
- g_assert_cmpstr (gcm_edid_get_monitor_name (edid), ==, NULL);
- g_assert_cmpstr (gcm_edid_get_vendor_name (edid), ==, "IBM France");
- g_assert_cmpstr (gcm_edid_get_serial_number (edid), ==, NULL);
- g_assert_cmpstr (gcm_edid_get_eisa_id (edid), ==, "LTN154P2-L05");
- g_assert_cmpstr (gcm_edid_get_checksum (edid), ==, "e1865128c7cd5e5ed49ecfc8102f6f9c");
- g_assert_cmpstr (gcm_edid_get_pnp_id (edid), ==, "IBM");
- g_assert_cmpint (gcm_edid_get_height (edid), ==, 21);
- g_assert_cmpint (gcm_edid_get_width (edid), ==, 33);
- g_assert_cmpfloat (gcm_edid_get_gamma (edid), >=, 2.2f - 0.01);
- g_assert_cmpfloat (gcm_edid_get_gamma (edid), <, 2.2f + 0.01);
- g_free (data);
-
- g_object_unref (edid);
-}
-
-int
-main (int argc, char **argv)
-{
- gtk_init (&argc, &argv);
- g_test_init (&argc, &argv, NULL);
-
- g_test_add_func ("/color/dmi", gcm_test_dmi_func);
- g_test_add_func ("/color/edid", gcm_test_edid_func);
-
- return g_test_run ();
-}
-
diff --git a/plugins/color/generate-tz-header.py b/plugins/color/generate-tz-header.py
new file mode 100755
index 00000000..cc1d3990
--- /dev/null
+++ b/plugins/color/generate-tz-header.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python3
+
+import re
+
+d = {}
+
+
+with open("/usr/share/zoneinfo/zone.tab", "r") as f:
+ for line in f:
+ if line.startswith("#"):
+ continue
+
+ res = re.search(r"([A-Z]{2})\s([0-9-+]+)\s([\w/_\-]+)\s", line)
+ code, coords, tz = res.groups()
+
+ res = re.search(r"([+-]{1})([0-9]+)([+-]{1})([0-9]+)", coords)
+ lat_sign, lat_val, long_sign, long_val = res.groups()
+
+ lat_str = lat_sign + lat_val[0:2] + "." + lat_val[2:]
+ long_str = long_sign + long_val[0:3] + "." + long_val[3:]
+
+ lat = float(lat_str)
+ long = float(long_str)
+
+ d[tz] = [lat, long]
+
+header = """
+// Generated from /usr/share/zoneinfo/zone.tab, used by csd-nightlight.c to calculate sunrise and sunset based on the system timezone
+
+typedef struct
+{
+ const char *timezone;
+ double latitude;
+ double longitude;
+} TZCoords;
+
+static TZCoords tz_coord_list[] = {
+"""
+
+for zone in sorted(d.keys()):
+ latitude, longitude = d[zone]
+
+ header += " { \"%s\", %f, %f },\n" % (zone, latitude, longitude)
+
+header += "};"
+
+with open("tz-coords.h", "w") as f:
+ f.write(header)
+
+quit()
\ No newline at end of file
diff --git a/plugins/color/gnome-datetime-source.c b/plugins/color/gnome-datetime-source.c
new file mode 100644
index 00000000..287ba2dd
--- /dev/null
+++ b/plugins/color/gnome-datetime-source.c
@@ -0,0 +1,286 @@
+/* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*-
+ * gdatetime-source.c - copy&paste from https://bugzilla.gnome.org/show_bug.cgi?id=655129
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ *
+ * Author: Colin Walters
+ */
+
+#include "config.h"
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-datetime-source.h"
+
+#if HAVE_TIMERFD
+#include
+#include
+#include
+#endif
+
+typedef struct _GDateTimeSource GDateTimeSource;
+struct _GDateTimeSource
+{
+ GSource source;
+
+ gint64 real_expiration;
+ gint64 wakeup_expiration;
+
+ gboolean cancel_on_set : 1;
+ gboolean initially_expired : 1;
+
+ GPollFD pollfd;
+};
+
+static inline void
+g_datetime_source_reschedule (GDateTimeSource *datetime_source,
+ gint64 from_monotonic)
+{
+ datetime_source->wakeup_expiration = from_monotonic + G_TIME_SPAN_SECOND;
+}
+
+static gboolean
+g_datetime_source_is_expired (GDateTimeSource *datetime_source)
+{
+ gint64 real_now;
+ gint64 monotonic_now;
+
+ real_now = g_get_real_time ();
+ monotonic_now = g_source_get_time ((GSource*)datetime_source);
+
+ if (datetime_source->initially_expired)
+ return TRUE;
+
+ if (datetime_source->real_expiration <= real_now)
+ return TRUE;
+
+ /* We can't really detect without system support when things
+ * change; so just trigger every second (i.e. our wakeup
+ * expiration)
+ */
+ if (datetime_source->cancel_on_set && monotonic_now >= datetime_source->wakeup_expiration)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* In prepare, we're just checking the monotonic time against
+ * our projected wakeup.
+ */
+static gboolean
+g_datetime_source_prepare (GSource *source,
+ gint *timeout)
+{
+ GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+ gint64 monotonic_now;
+
+#if HAVE_TIMERFD
+ if (datetime_source->pollfd.fd != -1) {
+ *timeout = -1;
+ return datetime_source->initially_expired; /* Should be TRUE at most one time, FALSE forever after */
+ }
+#endif
+
+ monotonic_now = g_source_get_time (source);
+
+ if (monotonic_now < datetime_source->wakeup_expiration) {
+ /* Round up to ensure that we don't try again too early */
+ *timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000;
+ return FALSE;
+ }
+
+ *timeout = 0;
+ return g_datetime_source_is_expired (datetime_source);
+}
+
+/* In check, we're looking at the wall clock.
+ */
+static gboolean
+g_datetime_source_check (GSource *source)
+{
+ GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+
+#if HAVE_TIMERFD
+ if (datetime_source->pollfd.fd != -1)
+ return datetime_source->pollfd.revents != 0;
+#endif
+
+ if (g_datetime_source_is_expired (datetime_source))
+ return TRUE;
+
+ g_datetime_source_reschedule (datetime_source, g_source_get_time (source));
+
+ return FALSE;
+}
+
+static gboolean
+g_datetime_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+
+ datetime_source->initially_expired = FALSE;
+
+ if (!callback) {
+ g_warning ("Timeout source dispatched without callback\n"
+ "You must call g_source_set_callback().");
+ return FALSE;
+ }
+
+ (callback) (user_data);
+
+ /* Always false as this source is documented to run once */
+ return FALSE;
+}
+
+static void
+g_datetime_source_finalize (GSource *source)
+{
+#if HAVE_TIMERFD
+ GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+ if (datetime_source->pollfd.fd != -1)
+ close (datetime_source->pollfd.fd);
+#endif
+}
+
+static GSourceFuncs g_datetime_source_funcs = {
+ g_datetime_source_prepare,
+ g_datetime_source_check,
+ g_datetime_source_dispatch,
+ g_datetime_source_finalize
+};
+
+#if HAVE_TIMERFD
+static gboolean
+g_datetime_source_init_timerfd (GDateTimeSource *datetime_source,
+ gint64 expected_now_seconds,
+ gint64 unix_seconds)
+{
+ struct itimerspec its;
+ int settime_flags;
+
+ datetime_source->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC);
+ if (datetime_source->pollfd.fd == -1)
+ return FALSE;
+
+ memset (&its, 0, sizeof (its));
+ its.it_value.tv_sec = (time_t) unix_seconds;
+
+ /* http://article.gmane.org/gmane.linux.kernel/1132138 */
+#ifndef TFD_TIMER_CANCEL_ON_SET
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
+
+ settime_flags = TFD_TIMER_ABSTIME;
+ if (datetime_source->cancel_on_set)
+ settime_flags |= TFD_TIMER_CANCEL_ON_SET;
+
+ if (timerfd_settime (datetime_source->pollfd.fd, settime_flags, &its, NULL) < 0) {
+ close (datetime_source->pollfd.fd);
+ datetime_source->pollfd.fd = -1;
+ return FALSE;
+ }
+
+ /* Now we need to check that the clock didn't go backwards before we
+ * had the timerfd set up. See
+ * https://bugzilla.gnome.org/show_bug.cgi?id=655129
+ */
+ clock_gettime (CLOCK_REALTIME, &its.it_value);
+ if (its.it_value.tv_sec < expected_now_seconds)
+ datetime_source->initially_expired = TRUE;
+
+ datetime_source->pollfd.events = G_IO_IN;
+
+ g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd);
+
+ return TRUE;
+}
+#endif
+
+/**
+ * _gnome_date_time_source_new:
+ * @now: The expected current time
+ * @expiry: Time to await
+ * @cancel_on_set: Also invoke callback if the system clock changes discontiguously
+ *
+ * This function is designed for programs that want to schedule an
+ * event based on real (wall clock) time, as returned by
+ * g_get_real_time(). For example, HOUR:MINUTE wall-clock displays
+ * and calendaring software. The callback will be invoked when the
+ * specified wall clock time @expiry is reached. This includes
+ * events such as the system clock being set past the given time.
+ *
+ * Compare versus g_timeout_source_new() which is defined to use
+ * monotonic time as returned by g_get_monotonic_time().
+ *
+ * The parameter @now is necessary to avoid a race condition in
+ * between getting the current time and calling this function.
+ *
+ * If @cancel_on_set is given, the callback will also be invoked at
+ * most a second after the system clock is changed. This includes
+ * being set backwards or forwards, and system
+ * resume from suspend. Not all operating systems allow detecting all
+ * relevant events efficiently - this function may cause the process
+ * to wake up once a second in those cases.
+ *
+ * A wall clock display should use @cancel_on_set; a calendaring
+ * program shouldn't need to.
+ *
+ * Note that the return value from the associated callback will be
+ * ignored; this is a one time watch.
+ *
+ * This function currently does not detect time zone
+ * changes. On Linux, your program should also monitor the
+ * /etc/timezone file using
+ * #GFileMonitor.
+ *
+ * Clock exampleFIXME: MISSING XINCLUDE CONTENT
+ *
+ * Return value: A newly-constructed #GSource
+ *
+ * Since: 2.30
+ **/
+GSource *
+_gnome_datetime_source_new (GDateTime *now,
+ GDateTime *expiry,
+ gboolean cancel_on_set)
+{
+ GDateTimeSource *datetime_source;
+ gint64 unix_seconds;
+
+ unix_seconds = g_date_time_to_unix (expiry);
+
+ datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource));
+
+ datetime_source->cancel_on_set = cancel_on_set;
+
+#if HAVE_TIMERFD
+ {
+ gint64 expected_now_seconds = g_date_time_to_unix (now);
+ if (g_datetime_source_init_timerfd (datetime_source, expected_now_seconds, unix_seconds))
+ return (GSource*)datetime_source;
+ /* Fall through to non-timerfd code */
+ }
+#endif
+
+ datetime_source->real_expiration = unix_seconds * 1000000;
+ g_datetime_source_reschedule (datetime_source, g_get_monotonic_time ());
+
+ return (GSource*)datetime_source;
+}
+
diff --git a/plugins/color/gnome-datetime-source.h b/plugins/color/gnome-datetime-source.h
new file mode 100644
index 00000000..e9ecbf00
--- /dev/null
+++ b/plugins/color/gnome-datetime-source.h
@@ -0,0 +1,38 @@
+/* gnome-rr.h
+ *
+ * Copyright 2011, Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Colin Walters
+ */
+
+#ifndef GNOME_DATETIME_SOURCE_H
+#define GNOME_DATETIME_SOURCE_H
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#error This is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API
+#endif
+
+#include
+
+GSource *_gnome_datetime_source_new (GDateTime *now,
+ GDateTime *expiry,
+ gboolean cancel_on_set);
+
+#endif /* GNOME_DATETIME_SOURCE_H */
diff --git a/plugins/color/main.c b/plugins/color/main.c
index f48b7eb7..70125e13 100644
--- a/plugins/color/main.c
+++ b/plugins/color/main.c
@@ -3,11 +3,13 @@
#define STOP csd_color_manager_stop
#define MANAGER CsdColorManager
+
+
// Setting this to TRUE makes the plugin register
// with CSM before starting.
// Setting this to FALSE makes CSM wait for the plugin to be started
// before initializing the next phase.
-#define REGISTER_BEFORE_STARTING TRUE
+#define REGISTER_BEFORE_STARTING FALSE
// TRUE if the plugin sends notifications
#define INIT_LIBNOTIFY TRUE
@@ -16,7 +18,7 @@
#define FORCE_GDK_SCALE TRUE
// This plugin must run under x11/xwayland
-#define FORCE_X11_BACKEND TRUE
+#define FORCE_X11_BACKEND FALSE
#include "csd-color-manager.h"
diff --git a/plugins/color/meson.build b/plugins/color/meson.build
index f5dc3185..42638cfd 100644
--- a/plugins/color/meson.build
+++ b/plugins/color/meson.build
@@ -1,12 +1,23 @@
-plugin_name = 'color'
-
-color_sources = [
- 'csd-color-manager.c',
- 'gcm-profile-store.c',
- 'gcm-dmi.c',
- 'gcm-edid.c',
- 'main.c',
-]
+plugin_name='color'
+
+
+built_sources = gnome.gdbus_codegen(
+ 'cinnamon-session-dbus',
+ sources: 'org.gnome.SessionManager.xml',
+ interface_prefix: 'org.',
+)
+
+sources = files(
+ 'ccm-edid.c',
+ 'gnome-datetime-source.c',
+ 'csd-color-calibrate.c',
+ 'csd-color-manager.c',
+ 'csd-color-profiles.c',
+ 'csd-color-state.c',
+ 'csd-night-light.c',
+ 'csd-night-light-common.c',
+ 'main.c'
+)
color_deps = [
canberra,
@@ -20,17 +31,18 @@ color_deps = [
]
executable(
- 'csd-color',
- color_sources,
- include_directories: [include_dirs, common_inc],
- dependencies: color_deps,
- c_args: [
- '-DPLUGIN_NAME="@0@"'.format(plugin_name),
- '-DBINDIR="@0@"'.format(bindir),
- ],
- install_rpath: join_paths(prefix, apilibdir),
- install: true,
- install_dir: libexecdir,
+ 'csd-' + plugin_name,
+ sources + built_sources,
+ include_directories: [include_dirs, common_inc],
+ dependencies: color_deps,
+ c_args: [
+ '-DPLUGIN_NAME="@0@"'.format(plugin_name),
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
+ '-DBINDIR="@0@"'.format(bindir),
+ ],
+ install: true,
+ install_rpath: join_paths(prefix, apilibdir),
+ install_dir: libexecdir
)
meson.add_install_script(ln_script, libexecdir, bindir, 'csd-color')
@@ -38,25 +50,30 @@ if libexecdir != pkglibdir
meson.add_install_script(ln_script, libexecdir, pkglibdir, 'csd-color')
endif
-test_color_sources = [
- 'gcm-dmi.c',
- 'gcm-edid.c',
- 'gcm-self-test.c',
-]
-
-executable(
- 'test-color',
- test_color_sources,
- dependencies: color_deps,
- c_args: [
- '-DTESTDATADIR="@0@"'.format(join_paths(meson.current_source_dir(), 'test-data')),
- ],
- install: false,
-)
-
configure_file(
input: 'cinnamon-settings-daemon-color.desktop.in',
output: 'cinnamon-settings-daemon-color.desktop',
configuration: desktop_conf,
install_dir: autostartdir,
)
+
+sources = files(
+ 'ccm-edid.c',
+ 'ccm-self-test.c',
+ 'gnome-datetime-source.c',
+ 'csd-night-light.c',
+ 'csd-night-light-common.c'
+)
+
+# test_unit = 'ccm-self-test'
+
+# exe = executable(
+# test_unit,
+# sources,
+# include_directories: include_dirs,
+# dependencies: color_deps,
+# c_args: '-DTESTDATADIR="@0@"'.format(join_paths(meson.current_source_dir(), 'test-data'))
+# )
+
+# envs = ['GSETTINGS_SCHEMA_DIR=@0@'.format(join_paths(meson.build_root(), 'data'))]
+# test(test_unit, exe, env: envs)
diff --git a/plugins/color/org.cinnamon.SettingsDaemon.Color.desktop.in b/plugins/color/org.cinnamon.SettingsDaemon.Color.desktop.in
new file mode 100644
index 00000000..13ce7b4a
--- /dev/null
+++ b/plugins/color/org.cinnamon.SettingsDaemon.Color.desktop.in
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=Cinnamon Settings Daemon's color plugin
+Exec=@libexecdir@/csd-color
+OnlyShowIn=GNOME;
+NoDisplay=true
+X-GNOME-Autostart-Phase=Initialization
+X-GNOME-Autostart-Notify=true
+X-GNOME-AutoRestart=true
+X-GNOME-HiddenUnderSystemd=@systemd_hidden@
diff --git a/plugins/color/org.gnome.SessionManager.xml b/plugins/color/org.gnome.SessionManager.xml
new file mode 100644
index 00000000..db0e28de
--- /dev/null
+++ b/plugins/color/org.gnome.SessionManager.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ If true, the session is currently in the
+ foreground and available for user input.
+
+
+
+
+
diff --git a/plugins/color/tz-coords.h b/plugins/color/tz-coords.h
new file mode 100644
index 00000000..db474fb4
--- /dev/null
+++ b/plugins/color/tz-coords.h
@@ -0,0 +1,430 @@
+
+// Generated from /usr/share/zoneinfo/zone.tab, used by csd-nightlight.c to calculate sunrise and sunset based on the system timezone
+
+typedef struct
+{
+ const gchar *timezone;
+ double latitude;
+ double longitude;
+} TZCoords;
+
+static TZCoords tz_coord_list[] = {
+ { "Africa/Abidjan", 5.190000, -4.020000 },
+ { "Africa/Accra", 5.330000, -0.130000 },
+ { "Africa/Addis_Ababa", 9.020000, 38.420000 },
+ { "Africa/Algiers", 36.470000, 3.030000 },
+ { "Africa/Asmara", 15.200000, 38.530000 },
+ { "Africa/Bamako", 12.390000, -8.000000 },
+ { "Africa/Bangui", 4.220000, 18.350000 },
+ { "Africa/Banjul", 13.280000, -16.390000 },
+ { "Africa/Bissau", 11.510000, -15.350000 },
+ { "Africa/Blantyre", -15.470000, 35.000000 },
+ { "Africa/Brazzaville", -4.160000, 15.170000 },
+ { "Africa/Bujumbura", -3.230000, 29.220000 },
+ { "Africa/Cairo", 30.030000, 31.150000 },
+ { "Africa/Casablanca", 33.390000, -7.350000 },
+ { "Africa/Ceuta", 35.530000, -5.190000 },
+ { "Africa/Conakry", 9.310000, -13.430000 },
+ { "Africa/Dakar", 14.400000, -17.260000 },
+ { "Africa/Dar_es_Salaam", -6.480000, 39.170000 },
+ { "Africa/Djibouti", 11.360000, 43.090000 },
+ { "Africa/Douala", 4.030000, 9.420000 },
+ { "Africa/El_Aaiun", 27.090000, -13.120000 },
+ { "Africa/Freetown", 8.300000, -13.150000 },
+ { "Africa/Gaborone", -24.390000, 25.550000 },
+ { "Africa/Harare", -17.500000, 31.030000 },
+ { "Africa/Johannesburg", -26.150000, 28.000000 },
+ { "Africa/Juba", 4.510000, 31.370000 },
+ { "Africa/Kampala", 0.190000, 32.250000 },
+ { "Africa/Khartoum", 15.360000, 32.320000 },
+ { "Africa/Kigali", -1.570000, 30.040000 },
+ { "Africa/Kinshasa", -4.180000, 15.180000 },
+ { "Africa/Lagos", 6.270000, 3.240000 },
+ { "Africa/Libreville", 0.230000, 9.270000 },
+ { "Africa/Lome", 6.080000, 1.130000 },
+ { "Africa/Luanda", -8.480000, 13.140000 },
+ { "Africa/Lubumbashi", -11.400000, 27.280000 },
+ { "Africa/Lusaka", -15.250000, 28.170000 },
+ { "Africa/Malabo", 3.450000, 8.470000 },
+ { "Africa/Maputo", -25.580000, 32.350000 },
+ { "Africa/Maseru", -29.280000, 27.300000 },
+ { "Africa/Mbabane", -26.180000, 31.060000 },
+ { "Africa/Mogadishu", 2.040000, 45.220000 },
+ { "Africa/Monrovia", 6.180000, -10.470000 },
+ { "Africa/Nairobi", -1.170000, 36.490000 },
+ { "Africa/Ndjamena", 12.070000, 15.030000 },
+ { "Africa/Niamey", 13.310000, 2.070000 },
+ { "Africa/Nouakchott", 18.060000, -15.570000 },
+ { "Africa/Ouagadougou", 12.220000, -1.310000 },
+ { "Africa/Porto-Novo", 6.290000, 2.370000 },
+ { "Africa/Sao_Tome", 0.200000, 6.440000 },
+ { "Africa/Tripoli", 32.540000, 13.110000 },
+ { "Africa/Tunis", 36.480000, 10.110000 },
+ { "Africa/Windhoek", -22.340000, 17.060000 },
+ { "America/Adak", 51.524800, -176.392900 },
+ { "America/Anchorage", 61.130500, -149.540100 },
+ { "America/Anguilla", 18.120000, -63.040000 },
+ { "America/Antigua", 17.030000, -61.480000 },
+ { "America/Araguaina", -7.120000, -48.120000 },
+ { "America/Argentina/Buenos_Aires", -34.360000, -58.270000 },
+ { "America/Argentina/Catamarca", -28.280000, -65.470000 },
+ { "America/Argentina/Cordoba", -31.240000, -64.110000 },
+ { "America/Argentina/Jujuy", -24.110000, -65.180000 },
+ { "America/Argentina/La_Rioja", -29.260000, -66.510000 },
+ { "America/Argentina/Mendoza", -32.530000, -68.490000 },
+ { "America/Argentina/Rio_Gallegos", -51.380000, -69.130000 },
+ { "America/Argentina/Salta", -24.470000, -65.250000 },
+ { "America/Argentina/San_Juan", -31.320000, -68.310000 },
+ { "America/Argentina/San_Luis", -33.190000, -66.210000 },
+ { "America/Argentina/Tucuman", -26.490000, -65.130000 },
+ { "America/Argentina/Ushuaia", -54.480000, -68.180000 },
+ { "America/Aruba", 12.300000, -69.580000 },
+ { "America/Asuncion", -25.160000, -57.400000 },
+ { "America/Atikokan", 48.453100, -91.371800 },
+ { "America/Bahia", -12.590000, -38.310000 },
+ { "America/Bahia_Banderas", 20.480000, -105.150000 },
+ { "America/Barbados", 13.060000, -59.370000 },
+ { "America/Belem", -1.270000, -48.290000 },
+ { "America/Belize", 17.300000, -88.120000 },
+ { "America/Blanc-Sablon", 51.250000, -57.070000 },
+ { "America/Boa_Vista", 2.490000, -60.400000 },
+ { "America/Bogota", 4.360000, -74.050000 },
+ { "America/Boise", 43.364900, -116.120900 },
+ { "America/Cambridge_Bay", 69.065000, -105.031000 },
+ { "America/Campo_Grande", -20.270000, -54.370000 },
+ { "America/Cancun", 21.050000, -86.460000 },
+ { "America/Caracas", 10.300000, -66.560000 },
+ { "America/Cayenne", 4.560000, -52.200000 },
+ { "America/Cayman", 19.180000, -81.230000 },
+ { "America/Chicago", 41.510000, -87.390000 },
+ { "America/Chihuahua", 28.380000, -106.050000 },
+ { "America/Ciudad_Juarez", 31.440000, -106.290000 },
+ { "America/Costa_Rica", 9.560000, -84.050000 },
+ { "America/Creston", 49.060000, -116.310000 },
+ { "America/Cuiaba", -15.350000, -56.050000 },
+ { "America/Curacao", 12.110000, -69.000000 },
+ { "America/Danmarkshavn", 76.460000, -18.400000 },
+ { "America/Dawson", 64.040000, -139.250000 },
+ { "America/Dawson_Creek", 55.460000, -120.140000 },
+ { "America/Denver", 39.442100, -104.590300 },
+ { "America/Detroit", 42.195300, -83.024500 },
+ { "America/Dominica", 15.180000, -61.240000 },
+ { "America/Edmonton", 53.330000, -113.280000 },
+ { "America/Eirunepe", -6.400000, -69.520000 },
+ { "America/El_Salvador", 13.420000, -89.120000 },
+ { "America/Fort_Nelson", 58.480000, -122.420000 },
+ { "America/Fortaleza", -3.430000, -38.300000 },
+ { "America/Glace_Bay", 46.120000, -59.570000 },
+ { "America/Goose_Bay", 53.200000, -60.250000 },
+ { "America/Grand_Turk", 21.280000, -71.080000 },
+ { "America/Grenada", 12.030000, -61.450000 },
+ { "America/Guadeloupe", 16.140000, -61.320000 },
+ { "America/Guatemala", 14.380000, -90.310000 },
+ { "America/Guayaquil", -2.100000, -79.500000 },
+ { "America/Guyana", 6.480000, -58.100000 },
+ { "America/Halifax", 44.390000, -63.360000 },
+ { "America/Havana", 23.080000, -82.220000 },
+ { "America/Hermosillo", 29.040000, -110.580000 },
+ { "America/Indiana/Indianapolis", 39.460600, -86.092900 },
+ { "America/Indiana/Knox", 41.174500, -86.373000 },
+ { "America/Indiana/Marengo", 38.223200, -86.204100 },
+ { "America/Indiana/Petersburg", 38.293100, -87.164300 },
+ { "America/Indiana/Tell_City", 37.571100, -86.454100 },
+ { "America/Indiana/Vevay", 38.445200, -85.040200 },
+ { "America/Indiana/Vincennes", 38.403800, -87.314300 },
+ { "America/Indiana/Winamac", 41.030500, -86.361100 },
+ { "America/Inuvik", 68.205900, -133.430000 },
+ { "America/Iqaluit", 63.440000, -68.280000 },
+ { "America/Jamaica", 17.580500, -76.473600 },
+ { "America/Juneau", 58.180700, -134.251100 },
+ { "America/Kentucky/Louisville", 38.151500, -85.453400 },
+ { "America/Kentucky/Monticello", 36.494700, -84.505700 },
+ { "America/Kralendijk", 12.090300, -68.163600 },
+ { "America/La_Paz", -16.300000, -68.090000 },
+ { "America/Lima", -12.030000, -77.030000 },
+ { "America/Los_Angeles", 34.030800, -118.143400 },
+ { "America/Lower_Princes", 18.030500, -63.025000 },
+ { "America/Maceio", -9.400000, -35.430000 },
+ { "America/Managua", 12.090000, -86.170000 },
+ { "America/Manaus", -3.080000, -60.010000 },
+ { "America/Marigot", 18.040000, -63.050000 },
+ { "America/Martinique", 14.360000, -61.050000 },
+ { "America/Matamoros", 25.500000, -97.300000 },
+ { "America/Mazatlan", 23.130000, -106.250000 },
+ { "America/Menominee", 45.062800, -87.365100 },
+ { "America/Merida", 20.580000, -89.370000 },
+ { "America/Metlakatla", 55.073700, -131.343500 },
+ { "America/Mexico_City", 19.240000, -99.090000 },
+ { "America/Miquelon", 47.030000, -56.200000 },
+ { "America/Moncton", 46.060000, -64.470000 },
+ { "America/Monterrey", 25.400000, -100.190000 },
+ { "America/Montevideo", -34.543300, -56.124500 },
+ { "America/Montserrat", 16.430000, -62.130000 },
+ { "America/Nassau", 25.050000, -77.210000 },
+ { "America/New_York", 40.425100, -74.002300 },
+ { "America/Nome", 64.300400, -165.242300 },
+ { "America/Noronha", -3.510000, -32.250000 },
+ { "America/North_Dakota/Beulah", 47.155100, -101.464000 },
+ { "America/North_Dakota/Center", 47.065900, -101.175700 },
+ { "America/North_Dakota/New_Salem", 46.504200, -101.243900 },
+ { "America/Nuuk", 64.110000, -51.440000 },
+ { "America/Ojinaga", 29.340000, -104.250000 },
+ { "America/Panama", 8.580000, -79.320000 },
+ { "America/Paramaribo", 5.500000, -55.100000 },
+ { "America/Phoenix", 33.265400, -112.042400 },
+ { "America/Port-au-Prince", 18.320000, -72.200000 },
+ { "America/Port_of_Spain", 10.390000, -61.310000 },
+ { "America/Porto_Velho", -8.460000, -63.540000 },
+ { "America/Puerto_Rico", 18.280600, -66.062200 },
+ { "America/Punta_Arenas", -53.090000, -70.550000 },
+ { "America/Rankin_Inlet", 62.490000, -92.045900 },
+ { "America/Recife", -8.030000, -34.540000 },
+ { "America/Regina", 50.240000, -104.390000 },
+ { "America/Resolute", 74.414400, -94.494500 },
+ { "America/Rio_Branco", -9.580000, -67.480000 },
+ { "America/Santarem", -2.260000, -54.520000 },
+ { "America/Santiago", -33.270000, -70.400000 },
+ { "America/Santo_Domingo", 18.280000, -69.540000 },
+ { "America/Sao_Paulo", -23.320000, -46.370000 },
+ { "America/Scoresbysund", 70.290000, -21.580000 },
+ { "America/Sitka", 57.103500, -135.180700 },
+ { "America/St_Barthelemy", 17.530000, -62.510000 },
+ { "America/St_Johns", 47.340000, -52.430000 },
+ { "America/St_Kitts", 17.180000, -62.430000 },
+ { "America/St_Lucia", 14.010000, -61.000000 },
+ { "America/St_Thomas", 18.210000, -64.560000 },
+ { "America/St_Vincent", 13.090000, -61.140000 },
+ { "America/Swift_Current", 50.170000, -107.500000 },
+ { "America/Tegucigalpa", 14.060000, -87.130000 },
+ { "America/Thule", 76.340000, -68.470000 },
+ { "America/Tijuana", 32.320000, -117.010000 },
+ { "America/Toronto", 43.390000, -79.230000 },
+ { "America/Tortola", 18.270000, -64.370000 },
+ { "America/Vancouver", 49.160000, -123.070000 },
+ { "America/Whitehorse", 60.430000, -135.030000 },
+ { "America/Winnipeg", 49.530000, -97.090000 },
+ { "America/Yakutat", 59.324900, -139.433800 },
+ { "Antarctica/Casey", -66.170000, 110.310000 },
+ { "Antarctica/Davis", -68.350000, 77.580000 },
+ { "Antarctica/DumontDUrville", -66.400000, 140.010000 },
+ { "Antarctica/Macquarie", -54.300000, 158.570000 },
+ { "Antarctica/Mawson", -67.360000, 62.530000 },
+ { "Antarctica/McMurdo", -77.500000, 166.360000 },
+ { "Antarctica/Palmer", -64.480000, -64.060000 },
+ { "Antarctica/Rothera", -67.340000, -68.080000 },
+ { "Antarctica/Syowa", -69.002200, 39.352400 },
+ { "Antarctica/Troll", -72.004100, 2.320600 },
+ { "Antarctica/Vostok", -78.240000, 106.540000 },
+ { "Arctic/Longyearbyen", 78.000000, 16.000000 },
+ { "Asia/Aden", 12.450000, 45.120000 },
+ { "Asia/Almaty", 43.150000, 76.570000 },
+ { "Asia/Amman", 31.570000, 35.560000 },
+ { "Asia/Anadyr", 64.450000, 177.290000 },
+ { "Asia/Aqtau", 44.310000, 50.160000 },
+ { "Asia/Aqtobe", 50.170000, 57.100000 },
+ { "Asia/Ashgabat", 37.570000, 58.230000 },
+ { "Asia/Atyrau", 47.070000, 51.560000 },
+ { "Asia/Baghdad", 33.210000, 44.250000 },
+ { "Asia/Bahrain", 26.230000, 50.350000 },
+ { "Asia/Baku", 40.230000, 49.510000 },
+ { "Asia/Bangkok", 13.450000, 100.310000 },
+ { "Asia/Barnaul", 53.220000, 83.450000 },
+ { "Asia/Beirut", 33.530000, 35.300000 },
+ { "Asia/Bishkek", 42.540000, 74.360000 },
+ { "Asia/Brunei", 4.560000, 114.550000 },
+ { "Asia/Chita", 52.030000, 113.280000 },
+ { "Asia/Choibalsan", 48.040000, 114.300000 },
+ { "Asia/Colombo", 6.560000, 79.510000 },
+ { "Asia/Damascus", 33.300000, 36.180000 },
+ { "Asia/Dhaka", 23.430000, 90.250000 },
+ { "Asia/Dili", -8.330000, 125.350000 },
+ { "Asia/Dubai", 25.180000, 55.180000 },
+ { "Asia/Dushanbe", 38.350000, 68.480000 },
+ { "Asia/Famagusta", 35.070000, 33.570000 },
+ { "Asia/Gaza", 31.300000, 34.280000 },
+ { "Asia/Hebron", 31.320000, 35.054200 },
+ { "Asia/Ho_Chi_Minh", 10.450000, 106.400000 },
+ { "Asia/Hong_Kong", 22.170000, 114.090000 },
+ { "Asia/Hovd", 48.010000, 91.390000 },
+ { "Asia/Irkutsk", 52.160000, 104.200000 },
+ { "Asia/Jakarta", -6.100000, 106.480000 },
+ { "Asia/Jayapura", -2.320000, 140.420000 },
+ { "Asia/Jerusalem", 31.465000, 35.132600 },
+ { "Asia/Kabul", 34.310000, 69.120000 },
+ { "Asia/Kamchatka", 53.010000, 158.390000 },
+ { "Asia/Karachi", 24.520000, 67.030000 },
+ { "Asia/Kathmandu", 27.430000, 85.190000 },
+ { "Asia/Khandyga", 62.392300, 135.331400 },
+ { "Asia/Kolkata", 22.320000, 88.220000 },
+ { "Asia/Krasnoyarsk", 56.010000, 92.500000 },
+ { "Asia/Kuala_Lumpur", 3.100000, 101.420000 },
+ { "Asia/Kuching", 1.330000, 110.200000 },
+ { "Asia/Kuwait", 29.200000, 47.590000 },
+ { "Asia/Macau", 22.115000, 113.323000 },
+ { "Asia/Magadan", 59.340000, 150.480000 },
+ { "Asia/Makassar", -5.070000, 119.240000 },
+ { "Asia/Manila", 14.350000, 121.000000 },
+ { "Asia/Muscat", 23.360000, 58.350000 },
+ { "Asia/Nicosia", 35.100000, 33.220000 },
+ { "Asia/Novokuznetsk", 53.450000, 87.070000 },
+ { "Asia/Novosibirsk", 55.020000, 82.550000 },
+ { "Asia/Omsk", 55.000000, 73.240000 },
+ { "Asia/Oral", 51.130000, 51.210000 },
+ { "Asia/Phnom_Penh", 11.330000, 104.550000 },
+ { "Asia/Pontianak", -0.020000, 109.200000 },
+ { "Asia/Pyongyang", 39.010000, 125.450000 },
+ { "Asia/Qatar", 25.170000, 51.320000 },
+ { "Asia/Qostanay", 53.120000, 63.370000 },
+ { "Asia/Qyzylorda", 44.480000, 65.280000 },
+ { "Asia/Riyadh", 24.380000, 46.430000 },
+ { "Asia/Sakhalin", 46.580000, 142.420000 },
+ { "Asia/Samarkand", 39.400000, 66.480000 },
+ { "Asia/Seoul", 37.330000, 126.580000 },
+ { "Asia/Shanghai", 31.140000, 121.280000 },
+ { "Asia/Singapore", 1.170000, 103.510000 },
+ { "Asia/Srednekolymsk", 67.280000, 153.430000 },
+ { "Asia/Taipei", 25.030000, 121.300000 },
+ { "Asia/Tashkent", 41.200000, 69.180000 },
+ { "Asia/Tbilisi", 41.430000, 44.490000 },
+ { "Asia/Tehran", 35.400000, 51.260000 },
+ { "Asia/Thimphu", 27.280000, 89.390000 },
+ { "Asia/Tokyo", 35.391600, 139.444100 },
+ { "Asia/Tomsk", 56.300000, 84.580000 },
+ { "Asia/Ulaanbaatar", 47.550000, 106.530000 },
+ { "Asia/Urumqi", 43.480000, 87.350000 },
+ { "Asia/Ust-Nera", 64.333700, 143.133600 },
+ { "Asia/Vientiane", 17.580000, 102.360000 },
+ { "Asia/Vladivostok", 43.100000, 131.560000 },
+ { "Asia/Yakutsk", 62.000000, 129.400000 },
+ { "Asia/Yangon", 16.470000, 96.100000 },
+ { "Asia/Yekaterinburg", 56.510000, 60.360000 },
+ { "Asia/Yerevan", 40.110000, 44.300000 },
+ { "Atlantic/Azores", 37.440000, -25.400000 },
+ { "Atlantic/Bermuda", 32.170000, -64.460000 },
+ { "Atlantic/Canary", 28.060000, -15.240000 },
+ { "Atlantic/Cape_Verde", 14.550000, -23.310000 },
+ { "Atlantic/Faroe", 62.010000, -6.460000 },
+ { "Atlantic/Madeira", 32.380000, -16.540000 },
+ { "Atlantic/Reykjavik", 64.090000, -21.510000 },
+ { "Atlantic/South_Georgia", -54.160000, -36.320000 },
+ { "Atlantic/St_Helena", -15.550000, -5.420000 },
+ { "Atlantic/Stanley", -51.420000, -57.510000 },
+ { "Australia/Adelaide", -34.550000, 138.350000 },
+ { "Australia/Brisbane", -27.280000, 153.020000 },
+ { "Australia/Broken_Hill", -31.570000, 141.270000 },
+ { "Australia/Darwin", -12.280000, 130.500000 },
+ { "Australia/Eucla", -31.430000, 128.520000 },
+ { "Australia/Hobart", -42.530000, 147.190000 },
+ { "Australia/Lindeman", -20.160000, 149.000000 },
+ { "Australia/Lord_Howe", -31.330000, 159.050000 },
+ { "Australia/Melbourne", -37.490000, 144.580000 },
+ { "Australia/Perth", -31.570000, 115.510000 },
+ { "Australia/Sydney", -33.520000, 151.130000 },
+ { "Europe/Amsterdam", 52.220000, 4.540000 },
+ { "Europe/Andorra", 42.300000, 1.310000 },
+ { "Europe/Astrakhan", 46.210000, 48.030000 },
+ { "Europe/Athens", 37.580000, 23.430000 },
+ { "Europe/Belgrade", 44.500000, 20.300000 },
+ { "Europe/Berlin", 52.300000, 13.220000 },
+ { "Europe/Bratislava", 48.090000, 17.070000 },
+ { "Europe/Brussels", 50.500000, 4.200000 },
+ { "Europe/Bucharest", 44.260000, 26.060000 },
+ { "Europe/Budapest", 47.300000, 19.050000 },
+ { "Europe/Busingen", 47.420000, 8.410000 },
+ { "Europe/Chisinau", 47.000000, 28.500000 },
+ { "Europe/Copenhagen", 55.400000, 12.350000 },
+ { "Europe/Dublin", 53.200000, -6.150000 },
+ { "Europe/Gibraltar", 36.080000, -5.210000 },
+ { "Europe/Guernsey", 49.271700, -2.321000 },
+ { "Europe/Helsinki", 60.100000, 24.580000 },
+ { "Europe/Isle_of_Man", 54.090000, -4.280000 },
+ { "Europe/Istanbul", 41.010000, 28.580000 },
+ { "Europe/Jersey", 49.110100, -2.062400 },
+ { "Europe/Kaliningrad", 54.430000, 20.300000 },
+ { "Europe/Kirov", 58.360000, 49.390000 },
+ { "Europe/Kyiv", 50.260000, 30.310000 },
+ { "Europe/Lisbon", 38.430000, -9.080000 },
+ { "Europe/Ljubljana", 46.030000, 14.310000 },
+ { "Europe/London", 51.303000, -0.073100 },
+ { "Europe/Luxembourg", 49.360000, 6.090000 },
+ { "Europe/Madrid", 40.240000, -3.410000 },
+ { "Europe/Malta", 35.540000, 14.310000 },
+ { "Europe/Mariehamn", 60.060000, 19.570000 },
+ { "Europe/Minsk", 53.540000, 27.340000 },
+ { "Europe/Monaco", 43.420000, 7.230000 },
+ { "Europe/Moscow", 55.452100, 37.370400 },
+ { "Europe/Oslo", 59.550000, 10.450000 },
+ { "Europe/Paris", 48.520000, 2.200000 },
+ { "Europe/Podgorica", 42.260000, 19.160000 },
+ { "Europe/Prague", 50.050000, 14.260000 },
+ { "Europe/Riga", 56.570000, 24.060000 },
+ { "Europe/Rome", 41.540000, 12.290000 },
+ { "Europe/Samara", 53.120000, 50.090000 },
+ { "Europe/San_Marino", 43.550000, 12.280000 },
+ { "Europe/Sarajevo", 43.520000, 18.250000 },
+ { "Europe/Saratov", 51.340000, 46.020000 },
+ { "Europe/Simferopol", 44.570000, 34.060000 },
+ { "Europe/Skopje", 41.590000, 21.260000 },
+ { "Europe/Sofia", 42.410000, 23.190000 },
+ { "Europe/Stockholm", 59.200000, 18.030000 },
+ { "Europe/Tallinn", 59.250000, 24.450000 },
+ { "Europe/Tirane", 41.200000, 19.500000 },
+ { "Europe/Ulyanovsk", 54.200000, 48.240000 },
+ { "Europe/Vaduz", 47.090000, 9.310000 },
+ { "Europe/Vatican", 41.540800, 12.271100 },
+ { "Europe/Vienna", 48.130000, 16.200000 },
+ { "Europe/Vilnius", 54.410000, 25.190000 },
+ { "Europe/Volgograd", 48.440000, 44.250000 },
+ { "Europe/Warsaw", 52.150000, 21.000000 },
+ { "Europe/Zagreb", 45.480000, 15.580000 },
+ { "Europe/Zurich", 47.230000, 8.320000 },
+ { "Indian/Antananarivo", -18.550000, 47.310000 },
+ { "Indian/Chagos", -7.200000, 72.250000 },
+ { "Indian/Christmas", -10.250000, 105.430000 },
+ { "Indian/Cocos", -12.100000, 96.550000 },
+ { "Indian/Comoro", -11.410000, 43.160000 },
+ { "Indian/Kerguelen", -49.211000, 70.130300 },
+ { "Indian/Mahe", -4.400000, 55.280000 },
+ { "Indian/Maldives", 4.100000, 73.300000 },
+ { "Indian/Mauritius", -20.100000, 57.300000 },
+ { "Indian/Mayotte", -12.470000, 45.140000 },
+ { "Indian/Reunion", -20.520000, 55.280000 },
+ { "Pacific/Apia", -13.500000, -171.440000 },
+ { "Pacific/Auckland", -36.520000, 174.460000 },
+ { "Pacific/Bougainville", -6.130000, 155.340000 },
+ { "Pacific/Chatham", -43.570000, -176.330000 },
+ { "Pacific/Chuuk", 7.250000, 151.470000 },
+ { "Pacific/Easter", -27.090000, -109.260000 },
+ { "Pacific/Efate", -17.400000, 168.250000 },
+ { "Pacific/Fakaofo", -9.220000, -171.140000 },
+ { "Pacific/Fiji", -18.080000, 178.250000 },
+ { "Pacific/Funafuti", -8.310000, 179.130000 },
+ { "Pacific/Galapagos", -0.540000, -89.360000 },
+ { "Pacific/Gambier", -23.080000, -134.570000 },
+ { "Pacific/Guadalcanal", -9.320000, 160.120000 },
+ { "Pacific/Guam", 13.280000, 144.450000 },
+ { "Pacific/Honolulu", 21.182500, -157.513000 },
+ { "Pacific/Kanton", -2.470000, -171.430000 },
+ { "Pacific/Kiritimati", 1.520000, -157.200000 },
+ { "Pacific/Kosrae", 5.190000, 162.590000 },
+ { "Pacific/Kwajalein", 9.050000, 167.200000 },
+ { "Pacific/Majuro", 7.090000, 171.120000 },
+ { "Pacific/Marquesas", -9.000000, -139.300000 },
+ { "Pacific/Midway", 28.130000, -177.220000 },
+ { "Pacific/Nauru", -0.310000, 166.550000 },
+ { "Pacific/Niue", -19.010000, -169.550000 },
+ { "Pacific/Norfolk", -29.030000, 167.580000 },
+ { "Pacific/Noumea", -22.160000, 166.270000 },
+ { "Pacific/Pago_Pago", -14.160000, -170.420000 },
+ { "Pacific/Palau", 7.200000, 134.290000 },
+ { "Pacific/Pitcairn", -25.040000, -130.050000 },
+ { "Pacific/Pohnpei", 6.580000, 158.130000 },
+ { "Pacific/Port_Moresby", -9.300000, 147.100000 },
+ { "Pacific/Rarotonga", -21.140000, -159.460000 },
+ { "Pacific/Saipan", 15.120000, 145.450000 },
+ { "Pacific/Tahiti", -17.320000, -149.340000 },
+ { "Pacific/Tarawa", 1.250000, 173.000000 },
+ { "Pacific/Tongatapu", -21.080000, -175.120000 },
+ { "Pacific/Wake", 19.170000, 166.370000 },
+ { "Pacific/Wallis", -13.180000, -176.100000 },
+};
\ No newline at end of file
diff --git a/plugins/common/daemon-skeleton-gtk.h b/plugins/common/daemon-skeleton-gtk.h
index cbb636b7..0a24450b 100644
--- a/plugins/common/daemon-skeleton-gtk.h
+++ b/plugins/common/daemon-skeleton-gtk.h
@@ -174,6 +174,23 @@ handle_signal (gpointer user_data)
return G_SOURCE_REMOVE;
}
+static void
+message_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ /* Make this look like normal console output */
+ if (log_level & G_LOG_LEVEL_DEBUG) {
+ g_autoptr(GDateTime) dt = g_date_time_new_now_local ();
+ g_autofree gchar *iso8601 = g_date_time_format (dt, "%F:%T");
+ printf ("csd-%s:%s: %s\n", PLUGIN_NAME, iso8601, message);
+ }
+ else {
+ printf ("%s: %s\n", g_get_prgname (), message);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -221,7 +238,7 @@ main (int argc, char **argv)
g_unix_signal_add (SIGTERM, (GSourceFunc) handle_signal, NULL);
if (verbose)
- g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+ g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL);
if (timeout > 0) {
guint id;
diff --git a/plugins/common/daemon-skeleton.h b/plugins/common/daemon-skeleton.h
index baec9737..1d86a651 100644
--- a/plugins/common/daemon-skeleton.h
+++ b/plugins/common/daemon-skeleton.h
@@ -171,6 +171,23 @@ handle_signal (gpointer user_data)
return G_SOURCE_REMOVE;
}
+static void
+message_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ /* Make this look like normal console output */
+ if (log_level & G_LOG_LEVEL_DEBUG) {
+ g_autoptr(GDateTime) dt = g_date_time_new_now_local ();
+ g_autofree gchar *iso8601 = g_date_time_format (dt, "%F:%T");
+ printf ("csd-%s:%s: %s\n", PLUGIN_NAME, iso8601, message);
+ }
+ else {
+ printf ("%s: %s\n", g_get_prgname (), message);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -208,10 +225,8 @@ main (int argc, char **argv)
g_unix_signal_add (SIGTERM, (GSourceFunc) handle_signal, loop);
- if (verbose) {
- g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
- setlinebuf (stdout);
- }
+ if (verbose)
+ g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL);
if (timeout > 0) {
guint id;
diff --git a/plugins/datetime/meson.build b/plugins/datetime/meson.build
index c4c2211d..11304fba 100644
--- a/plugins/datetime/meson.build
+++ b/plugins/datetime/meson.build
@@ -47,6 +47,7 @@ if polkit.found()
include_directories: [include_dirs, common_inc],
dependencies: datetime_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/housekeeping/meson.build b/plugins/housekeeping/meson.build
index 7ba58c5c..93dbea9e 100644
--- a/plugins/housekeeping/meson.build
+++ b/plugins/housekeeping/meson.build
@@ -25,6 +25,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: housekeeping_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/keyboard/meson.build b/plugins/keyboard/meson.build
index 454ffdba..25066e99 100644
--- a/plugins/keyboard/meson.build
+++ b/plugins/keyboard/meson.build
@@ -23,6 +23,7 @@ executable(
include_directories: [include_dirs, common_inc, include_enums],
dependencies: keyboard_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
'-DDATADIR="@0@"'.format(datadir),
],
diff --git a/plugins/media-keys/meson.build b/plugins/media-keys/meson.build
index a93629e7..4c82f6dd 100644
--- a/plugins/media-keys/meson.build
+++ b/plugins/media-keys/meson.build
@@ -25,6 +25,7 @@ executable(
include_directories: [include_dirs, common_inc, include_enums],
dependencies: media_keys_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/power/csd-power-manager.c b/plugins/power/csd-power-manager.c
index d2d29404..0df2cac6 100644
--- a/plugins/power/csd-power-manager.c
+++ b/plugins/power/csd-power-manager.c
@@ -180,7 +180,6 @@ struct CsdPowerManagerPrivate
GpmIdletime *idletime;
CsdPowerIdleMode current_idle_mode;
guint lid_close_safety_timer_id;
- GtkStatusIcon *status_icon;
guint xscreensaver_watchdog_timer_id;
gboolean is_virtual_machine;
gint fd_close_loop_end;
@@ -226,6 +225,10 @@ static void backlight_get_output_id (CsdPowerManager *manager, gint *xout,
static void device_properties_changed_cb (UpDevice *device, GParamSpec *pspec, CsdPowerManager *manager);
#endif
+static void connect_power_iface (CsdPowerManager *manager);
+static void connect_screen_iface (CsdPowerManager *manager);
+static void connect_keyboard_iface (CsdPowerManager *manager);
+
G_DEFINE_TYPE (CsdPowerManager, csd_power_manager, G_TYPE_OBJECT)
static gpointer manager_object = NULL;
@@ -709,7 +712,6 @@ engine_recalculate_state_icon (CsdPowerManager *manager)
/* show a different icon if we are disconnected */
icon = engine_get_icon (manager);
- gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
if (icon == NULL) {
/* none before, now none */
@@ -723,20 +725,12 @@ engine_recalculate_state_icon (CsdPowerManager *manager)
/* no icon before, now icon */
if (manager->priv->previous_icon == NULL) {
-
- /* set fallback icon */
- gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
- gtk_status_icon_set_from_gicon (manager->priv->status_icon, icon);
manager->priv->previous_icon = icon;
return TRUE;
}
/* icon before, now different */
if (!g_icon_equal (manager->priv->previous_icon, icon)) {
-
- /* set fallback icon */
- gtk_status_icon_set_from_gicon (manager->priv->status_icon, icon);
-
g_object_unref (manager->priv->previous_icon);
manager->priv->previous_icon = icon;
return TRUE;
@@ -756,22 +750,12 @@ engine_recalculate_state_summary (CsdPowerManager *manager)
summary = engine_get_summary (manager);
if (manager->priv->previous_summary == NULL) {
manager->priv->previous_summary = summary;
-
- /* set fallback tooltip */
- gtk_status_icon_set_tooltip_text (manager->priv->status_icon,
- summary);
-
return TRUE;
}
if (strcmp (manager->priv->previous_summary, summary) != 0) {
g_free (manager->priv->previous_summary);
manager->priv->previous_summary = summary;
-
- /* set fallback tooltip */
- gtk_status_icon_set_tooltip_text (manager->priv->status_icon,
- summary);
-
return TRUE;
}
g_debug ("no change");
@@ -2236,7 +2220,7 @@ external_monitor_is_connected (GnomeRRScreen *screen)
outputs = gnome_rr_screen_list_outputs (screen);
for (i = 0; outputs[i] != NULL; i++) {
if (randr_output_is_on (outputs[i]) &&
- !gnome_rr_output_is_laptop (outputs[i]))
+ !gnome_rr_output_is_builtin_display (outputs[i]))
return TRUE;
}
@@ -2321,7 +2305,7 @@ non_laptop_outputs_are_all_off (GnomeRRScreen *screen)
outputs = gnome_rr_screen_list_outputs (screen);
for (i = 0; outputs[i] != NULL; i++) {
- if (gnome_rr_output_is_laptop (outputs[i]))
+ if (gnome_rr_output_is_builtin_display (outputs[i]))
continue;
if (is_on (outputs[i]))
@@ -2520,9 +2504,7 @@ get_primary_output (CsdPowerManager *manager)
for (i = 0; outputs[i] != NULL; i++) {
if (gnome_rr_output_is_connected (outputs[i]) &&
- gnome_rr_output_is_laptop (outputs[i]) &&
- gnome_rr_output_get_backlight_min (outputs[i]) >= 0 &&
- gnome_rr_output_get_backlight_max (outputs[i]) > 0) {
+ gnome_rr_output_is_builtin_display (outputs[i])) {
output = outputs[i];
break;
}
@@ -2587,15 +2569,6 @@ backlight_helper_get_value (const gchar *argument, CsdPowerManager* manager,
gchar *command = NULL;
gchar *endptr = NULL;
-#ifndef __linux__
- /* non-Linux platforms won't have /sys/class/backlight */
- g_set_error_literal (error,
- CSD_POWER_MANAGER_ERROR,
- CSD_POWER_MANAGER_ERROR_FAILED,
- "The sysfs backlight helper is only for Linux");
- goto out;
-#endif
-
/* get the data */
command = g_strdup_printf (LIBEXECDIR "/csd-backlight-helper --%s %s",
argument,
@@ -2722,7 +2695,7 @@ backlight_get_output_id (CsdPowerManager *manager,
for (i = 0; outputs[i] != NULL; i++) {
if (gnome_rr_output_is_connected (outputs[i]) &&
- gnome_rr_output_is_laptop (outputs[i])) {
+ gnome_rr_output_is_builtin_display (outputs[i])) {
output = outputs[i];
break;
}
@@ -2752,8 +2725,7 @@ backlight_get_abs (CsdPowerManager *manager, GError **error)
/* prefer xbacklight */
output = get_primary_output (manager);
if (output != NULL) {
- return gnome_rr_output_get_backlight (output,
- error);
+ return gnome_rr_output_get_backlight (output);
}
}
@@ -2785,13 +2757,13 @@ backlight_get_percentage (CsdPowerManager *manager, GError **error)
output = get_primary_output (manager);
if (output != NULL) {
- min = gnome_rr_output_get_backlight_min (output);
- max = gnome_rr_output_get_backlight_max (output);
- now = gnome_rr_output_get_backlight (output, error);
- if (now < 0)
- goto out;
+ // min = gnome_rr_output_get_backlight_min (output);
+ // max = gnome_rr_output_get_backlight_max (output);
+ // now = gnome_rr_output_get_backlight (output);
+ // if (now < 0)
+ // goto out;
- value = ABS_TO_PERCENTAGE (min_abs_brightness (manager, min, max), max, now);
+ value = gnome_rr_output_get_backlight (output);
goto out;
}
}
@@ -2826,7 +2798,7 @@ backlight_get_min (CsdPowerManager *manager)
return 0;
/* get xbacklight value, which maybe non-zero */
- return gnome_rr_output_get_backlight_min (output);
+ return 0;
}
static gint
@@ -2841,7 +2813,7 @@ backlight_get_max (CsdPowerManager *manager, GError **error)
/* prefer xbacklight */
output = get_primary_output (manager);
if (output != NULL) {
- value = gnome_rr_output_get_backlight_max (output);
+ value = 100;
if (value < 0) {
g_set_error (error,
CSD_POWER_MANAGER_ERROR,
@@ -2884,15 +2856,15 @@ backlight_set_percentage (CsdPowerManager *manager,
/* prefer xbacklight */
output = get_primary_output (manager);
if (output != NULL) {
- min = gnome_rr_output_get_backlight_min (output);
- max = gnome_rr_output_get_backlight_max (output);
- if (min < 0 || max < 0) {
- g_warning ("no xrandr backlight capability");
- goto out;
- }
- discrete = CLAMP (PERCENTAGE_TO_ABS (min_abs_brightness (manager, min, max), max, value), min_abs_brightness (manager, min, max), max);
+ // min = gnome_rr_output_get_backlight_min (output);
+ // max = gnome_rr_output_get_backlight_max (output);
+ // if (min < 0 || max < 0) {
+ // g_warning ("no xrandr backlight capability");
+ // goto out;
+ // }
+ // discrete = CLAMP (PERCENTAGE_TO_ABS (min_abs_brightness (manager, min, max), max, value), min_abs_brightness (manager, min, max), max);
ret = gnome_rr_output_set_backlight (output,
- discrete,
+ value,
error);
goto out;
}
@@ -2943,19 +2915,19 @@ backlight_step_up (CsdPowerManager *manager, GError **error)
gnome_rr_output_get_name (output));
goto out;
}
- min = gnome_rr_output_get_backlight_min (output);
- max = gnome_rr_output_get_backlight_max (output);
- now = gnome_rr_output_get_backlight (output, error);
- if (now < 0)
- goto out;
-
- step = BRIGHTNESS_STEP_AMOUNT (max - min_abs_brightness (manager, min, max));
- discrete = MIN (now + step, max);
+ // min = gnome_rr_output_get_backlight_min (output);
+ // max = gnome_rr_output_get_backlight_max (output);
+ now = gnome_rr_output_get_backlight (output);
+ // if (now < 0)
+ // goto out;
+
+ // step = BRIGHTNESS_STEP_AMOUNT (max - min_abs_brightness (manager, min, max));
+ discrete = MIN (now + gnome_rr_output_get_min_backlight_step (output), 100);
ret = gnome_rr_output_set_backlight (output,
discrete,
error);
if (ret)
- percentage_value = ABS_TO_PERCENTAGE (min_abs_brightness (manager, min, max), max, discrete);
+ percentage_value = discrete;
goto out;
}
}
@@ -3010,18 +2982,18 @@ backlight_step_down (CsdPowerManager *manager, GError **error)
gnome_rr_output_get_name (output));
goto out;
}
- min = gnome_rr_output_get_backlight_min (output);
- max = gnome_rr_output_get_backlight_max (output);
- now = gnome_rr_output_get_backlight (output, error);
- if (now < 0)
- goto out;
- step = BRIGHTNESS_STEP_AMOUNT (max - min_abs_brightness (manager, min, max));
- discrete = MAX (now - step, min_abs_brightness (manager, min, max));
+ // min = gnome_rr_output_get_backlight_min (output);
+ // max = gnome_rr_output_get_backlight_max (output);
+ now = gnome_rr_output_get_backlight (output);
+ // if (now < 0)
+ // goto out;
+ step = gnome_rr_output_get_min_backlight_step (output);
+ discrete = MAX (now - step, 0);
ret = gnome_rr_output_set_backlight (output,
discrete,
error);
if (ret)
- percentage_value = ABS_TO_PERCENTAGE (min_abs_brightness (manager, min, max), max, discrete);
+ percentage_value = discrete;
goto out;
}
}
@@ -4275,30 +4247,27 @@ is_hardware_a_virtual_machine (void)
return ret;
}
-gboolean
-csd_power_manager_start (CsdPowerManager *manager,
- GError **error)
+static void
+on_rr_screen_acquired (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ CsdPowerManager *manager = user_data;
+ GError *error = NULL;
gboolean ret;
- g_debug ("Starting power manager");
cinnamon_settings_profile_start (NULL);
- /* coldplug the list of screens */
- manager->priv->x11_screen = gnome_rr_screen_new (gdk_screen_get_default (), error);
- if (manager->priv->x11_screen == NULL)
- return FALSE;
+ manager->priv->x11_screen = gnome_rr_screen_new_finish (result, &error);
+
+ if (error) {
+ g_warning ("Could not create GnomeRRScreen: %s\n", error->message);
+ g_error_free (error);
+ cinnamon_settings_profile_end (NULL);
+
+ return;
+ }
- /* Set up the logind proxy */
- manager->priv->logind_proxy =
- g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
- 0,
- NULL,
- LOGIND_DBUS_NAME,
- LOGIND_DBUS_PATH,
- LOGIND_DBUS_INTERFACE,
- NULL,
- error);
g_signal_connect (manager->priv->logind_proxy, "g-signal",
G_CALLBACK (logind_proxy_signal_cb),
manager);
@@ -4315,15 +4284,10 @@ csd_power_manager_start (CsdPowerManager *manager,
manager->priv->kbd_brightness_old = -1;
manager->priv->kbd_brightness_pre_dim = -1;
manager->priv->pre_dim_brightness = -1;
- manager->priv->settings = g_settings_new (CSD_POWER_SETTINGS_SCHEMA);
g_signal_connect (manager->priv->settings, "changed",
G_CALLBACK (engine_settings_key_changed_cb), manager);
- manager->priv->settings_screensaver = g_settings_new (CSD_SAVER_SETTINGS_SCHEMA);
- manager->priv->settings_xrandr = g_settings_new (CSD_XRANDR_SETTINGS_SCHEMA);
- manager->priv->settings_desktop_session = g_settings_new (CSD_SESSION_SETTINGS_SCHEMA);
g_signal_connect (manager->priv->settings_desktop_session, "changed",
G_CALLBACK (engine_settings_key_changed_cb), manager);
- manager->priv->settings_cinnamon_session = g_settings_new (CSD_CINNAMON_SESSION_SCHEMA);
manager->priv->inhibit_lid_switch_enabled =
g_settings_get_boolean (manager->priv->settings, "inhibit-lid-switch");
@@ -4356,16 +4320,6 @@ csd_power_manager_start (CsdPowerManager *manager,
G_CALLBACK (up_client_changed_cb), manager);
#endif
- /* use the fallback name from gnome-power-manager so the shell
- * blocks this, and uses the power extension instead */
- manager->priv->status_icon = gtk_status_icon_new ();
- gtk_status_icon_set_name (manager->priv->status_icon,
- "gnome-power-manager");
- /* TRANSLATORS: this is the title of the power manager status icon
- * that is only shown in fallback mode */
- gtk_status_icon_set_title (manager->priv->status_icon, _("Power Manager"));
- gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
-
/* connect to UPower for keyboard backlight control */
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
@@ -4397,9 +4351,12 @@ csd_power_manager_start (CsdPowerManager *manager,
session_presence_proxy_ready_cb,
manager);
- manager->priv->devices_array = g_ptr_array_new_with_free_func (g_object_unref);
manager->priv->canberra_context = ca_gtk_context_get_for_screen (gdk_screen_get_default ());
+ connect_power_iface (manager);
+ connect_screen_iface (manager);
+ connect_keyboard_iface (manager);
+
manager->priv->phone = gpm_phone_new ();
g_signal_connect (manager->priv->phone, "device-added",
G_CALLBACK (phone_device_added_cb), manager);
@@ -4458,10 +4415,10 @@ csd_power_manager_start (CsdPowerManager *manager,
/* ensure the default dpms timeouts are cleared */
ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
GNOME_RR_DPMS_ON,
- error);
+ &error);
if (!ret) {
- g_warning ("Failed set DPMS mode: %s", (*error)->message);
- g_clear_error (error);
+ g_warning ("Failed set DPMS mode: %s", error->message);
+ g_clear_error (&error);
}
/* coldplug the engine */
@@ -4483,8 +4440,39 @@ csd_power_manager_start (CsdPowerManager *manager,
NULL);
/* don't blank inside a VM */
manager->priv->is_virtual_machine = is_hardware_a_virtual_machine ();
+}
+
+gboolean
+csd_power_manager_start (CsdPowerManager *manager,
+ GError **error)
+{
+ g_debug ("Starting power manager");
+ cinnamon_settings_profile_start (NULL);
+
+ /* Set up the logind proxy */
+ manager->priv->logind_proxy =
+ g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ 0,
+ NULL,
+ LOGIND_DBUS_NAME,
+ LOGIND_DBUS_PATH,
+ LOGIND_DBUS_INTERFACE,
+ NULL,
+ error);
+
+ /* coldplug the list of screens */
+ gnome_rr_screen_new_async (gdk_screen_get_default (),
+ on_rr_screen_acquired, manager);
+
+ manager->priv->settings = g_settings_new (CSD_POWER_SETTINGS_SCHEMA);
+ manager->priv->settings_screensaver = g_settings_new (CSD_SAVER_SETTINGS_SCHEMA);
+ manager->priv->settings_xrandr = g_settings_new (CSD_XRANDR_SETTINGS_SCHEMA);
+ manager->priv->settings_desktop_session = g_settings_new (CSD_SESSION_SETTINGS_SCHEMA);
+ manager->priv->settings_cinnamon_session = g_settings_new (CSD_CINNAMON_SESSION_SCHEMA);
+ manager->priv->devices_array = g_ptr_array_new_with_free_func (g_object_unref);
cinnamon_settings_profile_end (NULL);
+
return TRUE;
}
@@ -4614,11 +4602,6 @@ csd_power_manager_stop (CsdPowerManager *manager)
manager->priv->idletime = NULL;
}
- if (manager->priv->status_icon != NULL) {
- g_object_unref (manager->priv->status_icon);
- manager->priv->status_icon = NULL;
- }
-
if (manager->priv->xscreensaver_watchdog_timer_id > 0) {
g_source_remove (manager->priv->xscreensaver_watchdog_timer_id);
manager->priv->xscreensaver_watchdog_timer_id = 0;
@@ -4993,123 +4976,135 @@ power_iface_handle_get_devices (CsdPower *object,
}
static void
-power_name_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
+connect_power_iface (CsdPowerManager *manager)
{
- CsdPower *iface;
- CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
-
- iface = csd_power_skeleton_new ();
-
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->power_iface,
"handle-get-primary-device",
G_CALLBACK (power_iface_handle_get_primary_device),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->power_iface,
"handle-get-devices",
G_CALLBACK (power_iface_handle_get_devices),
manager);
- g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
- connection,
- CSD_POWER_DBUS_PATH,
- NULL);
-
- manager->priv->power_iface = iface;
-
engine_recalculate_state (manager);
}
static void
-screen_name_acquired (GDBusConnection *connection,
+power_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- CsdScreen *iface;
+ CsdPower *iface;
CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
- iface = csd_screen_skeleton_new ();
+ iface = csd_power_skeleton_new ();
- g_signal_connect (iface,
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
+ connection,
+ CSD_POWER_DBUS_PATH,
+ NULL);
+
+ manager->priv->power_iface = iface;
+}
+
+static void
+connect_screen_iface (CsdPowerManager *manager)
+{
+ g_signal_connect (manager->priv->screen_iface,
"handle-get-percentage",
G_CALLBACK (screen_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->screen_iface,
"handle-set-percentage",
G_CALLBACK (screen_iface_set_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->screen_iface,
"handle-step-down",
G_CALLBACK (screen_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->screen_iface,
"handle-step-up",
G_CALLBACK (screen_iface_method_cb),
manager);
+ backlight_emit_changed (manager);
+}
+
+static void
+screen_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ CsdScreen *iface;
+ CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
+
+ iface = csd_screen_skeleton_new ();
+
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
connection,
CSD_POWER_DBUS_PATH,
NULL);
manager->priv->screen_iface = iface;
-
- backlight_emit_changed (manager);
}
static void
-keyboard_name_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
+connect_keyboard_iface (CsdPowerManager *manager)
{
- CsdKeyboard *iface;
- CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
-
- iface = csd_keyboard_skeleton_new ();
-
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-get-percentage",
G_CALLBACK (keyboard_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-set-percentage",
G_CALLBACK (keyboard_iface_set_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-step-down",
G_CALLBACK (keyboard_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-step-up",
G_CALLBACK (keyboard_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-get-step",
G_CALLBACK (keyboard_iface_method_cb),
manager);
- g_signal_connect (iface,
+ g_signal_connect (manager->priv->keyboard_iface,
"handle-toggle",
G_CALLBACK (keyboard_iface_method_cb),
manager);
+ upower_kbd_emit_changed (manager);
+}
+
+static void
+keyboard_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ CsdKeyboard *iface;
+ CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
+
+ iface = csd_keyboard_skeleton_new ();
+
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
connection,
CSD_POWER_DBUS_PATH,
NULL);
manager->priv->keyboard_iface = iface;
-
- upower_kbd_emit_changed (manager);
}
static void
diff --git a/plugins/power/meson.build b/plugins/power/meson.build
index a776cd29..61c1a240 100644
--- a/plugins/power/meson.build
+++ b/plugins/power/meson.build
@@ -61,6 +61,7 @@ executable(
dependencies: power_deps,
c_args: [
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
],
install_rpath: join_paths(prefix, apilibdir),
install: true,
diff --git a/plugins/print-notifications/meson.build b/plugins/print-notifications/meson.build
index eb65da4b..198f3208 100644
--- a/plugins/print-notifications/meson.build
+++ b/plugins/print-notifications/meson.build
@@ -23,6 +23,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: print_notifications_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/screensaver-proxy/meson.build b/plugins/screensaver-proxy/meson.build
index 904e7ba1..ea800c47 100644
--- a/plugins/screensaver-proxy/meson.build
+++ b/plugins/screensaver-proxy/meson.build
@@ -17,6 +17,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: screensaver_proxy_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/settings-remap/meson.build b/plugins/settings-remap/meson.build
index f288e1de..b6e3481f 100644
--- a/plugins/settings-remap/meson.build
+++ b/plugins/settings-remap/meson.build
@@ -16,6 +16,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: settings_remap_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/smartcard/meson.build b/plugins/smartcard/meson.build
index 0d9fd15d..24172546 100644
--- a/plugins/smartcard/meson.build
+++ b/plugins/smartcard/meson.build
@@ -19,6 +19,7 @@ executable(
include_directories: [include_dirs, common_inc],
dependencies: smartcard_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
],
install: true,
diff --git a/plugins/wacom/meson.build b/plugins/wacom/meson.build
index 342259b4..9e21b9c9 100644
--- a/plugins/wacom/meson.build
+++ b/plugins/wacom/meson.build
@@ -17,6 +17,7 @@ deps = [
]
cflags = [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
]
diff --git a/plugins/xsettings/meson.build b/plugins/xsettings/meson.build
index e79dac33..b9c9b7c7 100644
--- a/plugins/xsettings/meson.build
+++ b/plugins/xsettings/meson.build
@@ -32,6 +32,7 @@ executable(
include_directories: [include_dirs, common_inc, include_enums],
dependencies: xsettings_deps,
c_args: [
+ '-DG_LOG_DOMAIN="csd-@0@"'.format(plugin_name),
'-DPLUGIN_NAME="@0@"'.format(plugin_name),
'-DGTK_MODULES_DIRECTORY="@0@/@1@/cinnamon-settings-daemon-@2@/gtk-modules/"'.format(prefix, libdir, api_version),
],