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), ],