[gnome-settings-daemon] color: Move the device registration functionality from gcm-session to the color plugin
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] color: Move the device registration functionality from gcm-session to the color plugin
- Date: Sat, 11 Jun 2011 07:00:05 +0000 (UTC)
commit 0d278e7d66ee6f57d496d6de46d395f11a4d7cd4
Author: Richard Hughes <richard hughsie com>
Date: Wed Jun 1 10:15:31 2011 +0100
color: Move the device registration functionality from gcm-session to the color plugin
We have to monitor X devices in the session as colord as a system daemon and is
unable to access the per-session X screens and outputs without evil hacks.
When a display output is added or changed we:
* Register the display device with colord and add the required metadata entries
* Create an auto-EDID profile based from the chromacity data in the EDID
(if it does not already exist)
* Check to see if it's the default device, and if so, set the _ICC_PROFILE atom
* Make sure the default profile VCGT data for the device is loaded into X.
When a display is removed we:
* Unregister the device from colord.
acinclude.m4 | 12 +
configure.ac | 18 +-
plugins/color/Makefile.am | 67 +-
plugins/color/gcm-dmi.c | 170 +++
plugins/color/gcm-dmi.h | 60 +
plugins/color/gcm-edid.c | 457 ++++++++
plugins/color/gcm-edid.h | 83 ++
plugins/color/gcm-self-test.c | 145 +++
plugins/color/gcm-tables.c | 171 +++
plugins/color/gcm-tables.h | 67 ++
plugins/color/gsd-color-manager.c | 1303 ++++++++++++++++++++++-
plugins/color/gsd-color-manager.h | 7 +
plugins/color/test-data/LG-L225W-External.bin | Bin 0 -> 128 bytes
plugins/color/test-data/Lenovo-T61-Internal.bin | Bin 0 -> 128 bytes
14 files changed, 2525 insertions(+), 35 deletions(-)
---
diff --git a/acinclude.m4 b/acinclude.m4
index f2f8ec3..ec8da1f 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -134,3 +134,15 @@ AC_DEFUN([AS_AC_EXPAND],
prefix=$prefix_save
exec_prefix=$exec_prefix_save
])
+
+AC_DEFUN([AC_PATH_PNPIDS], [
+ AC_ARG_WITH(pnpids,
+ AS_HELP_STRING([--with-pnpids=PATH],[Path to the pnp.ids file @<:@auto@:>@]),
+ [ac_with_pnpids=$withval],
+ [ac_with_pnpids="/usr/share/hwdata/pnp.ids"])
+ AC_CHECK_FILE($ac_with_pnpids,has_pnpids=yes)
+ if test "x$has_pnpids" != "xyes"; then
+ AC_MSG_ERROR([*** Unable to find pnp.ids, use --with-pnpids to specify the path.])
+ fi
+ AC_DEFINE_UNQUOTED(PNPIDS_FILE, ["$ac_with_pnpids"], [Define the pnp.ids file path])
+])
diff --git a/configure.ac b/configure.ac
index e6c853f..ca2d2a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,6 +21,7 @@ AC_STDC_HEADERS
AC_PROG_CXX
AM_PROG_CC_C_O
AC_PROG_LIBTOOL
+AC_PATH_PNPIDS
AC_HEADER_STDC
@@ -100,7 +101,7 @@ PKG_CHECK_MODULES(GNOME_DESKTOP, gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VE
dnl ---------------------------------------------------------------------------
dnl - Check for colord
dnl ---------------------------------------------------------------------------
-PKG_CHECK_MODULES(COLORD, colord >= 0.1.8)
+PKG_CHECK_MODULES(COLORD, colord >= 0.1.9)
dnl ---------------------------------------------------------------------------
dnl - Check for libcanberra
@@ -114,11 +115,18 @@ AM_CONDITIONAL(HAVE_LIBCANBERRA, test "x$have_libcanberra" = "xtrue")
dnl ---------------------------------------------------------------------------
dnl - Check for LCMS2
dnl ---------------------------------------------------------------------------
-PKG_CHECK_MODULES(LCMS, lcms2, HAVE_LCMS="yes", HAVE_LCMS="no")
-if test "x$HAVE_LCMS" = "xyes"; then
+PKG_CHECK_MODULES(LCMS, lcms2 >= 2.2, have_new_lcms=yes, have_new_lcms=no)
+if test x$have_new_lcms = xyes; then
+ have_lcms="yes"
+ AC_DEFINE(HAVE_NEW_LCMS,1,[Got new lcms2])
+else
+ PKG_CHECK_MODULES(LCMS, lcms2, have_lcms="yes", have_lcms="no")
+fi
+if test "x$have_lcms" = "xyes"; then
AC_DEFINE(HAVE_LCMS, 1, [define if LCMS is available])
fi
+
dnl ---------------------------------------------------------------------------
dnl - Check for libnotify
dnl ---------------------------------------------------------------------------
@@ -618,7 +626,8 @@ echo "
dbus-1 system.d dir: ${DBUS_SYS_DIR}
PolicyKit support: ${HAVE_POLKIT}
- LCMS support: ${HAVE_LCMS}
+ LCMS support: ${have_lcms}
+ LCMS DICT support: ${have_new_lcms}
Libnotify support: ${have_libnotify}
Libcanberra support: ${have_libcanberra}
@@ -631,4 +640,5 @@ ${NSS_DATABASE:+\
System nssdb: ${NSS_DATABASE}
}\
Profiling support: ${enable_profiling}
+ pnp.ids filename: ${ac_with_pnpids}
"
diff --git a/plugins/color/Makefile.am b/plugins/color/Makefile.am
index 5d6174e..187ee7b 100644
--- a/plugins/color/Makefile.am
+++ b/plugins/color/Makefile.am
@@ -3,12 +3,18 @@ plugin_name = color
plugin_LTLIBRARIES = \
libcolor.la
-libcolor_la_SOURCES = \
- gcm-profile-store.c \
- gcm-profile-store.h \
- gsd-color-manager.c \
- gsd-color-manager.h \
- gsd-color-plugin.c \
+libcolor_la_SOURCES = \
+ gcm-profile-store.c \
+ gcm-profile-store.h \
+ gcm-dmi.c \
+ gcm-dmi.h \
+ gcm-edid.c \
+ gcm-edid.h \
+ gcm-tables.c \
+ gcm-tables.h \
+ gsd-color-manager.c \
+ gsd-color-manager.h \
+ gsd-color-plugin.c \
gsd-color-plugin.h
libcolor_la_CPPFLAGS = \
@@ -22,30 +28,61 @@ libcolor_la_CFLAGS = \
$(COLORD_CFLAGS) \
$(LIBCANBERRA_CFLAGS) \
$(LCMS_CFLAGS) \
+ $(GNOME_DESKTOP_CFLAGS) \
$(SETTINGS_PLUGIN_CFLAGS) \
$(AM_CFLAGS)
-libcolor_la_LDFLAGS = \
+libcolor_la_LDFLAGS = \
$(GSD_PLUGIN_LDFLAGS)
-libcolor_la_LIBADD = \
- $(COLORD_LIBS) \
- $(LIBCANBERRA_LIBS) \
- $(LCMS_LIBS) \
+libcolor_la_LIBADD = \
+ $(COLORD_LIBS) \
+ $(LIBCANBERRA_LIBS) \
+ $(GNOME_DESKTOP_LIBS) \
+ $(LCMS_LIBS) \
$(SETTINGS_PLUGIN_LIBS)
-plugin_in_files = \
+check_PROGRAMS = \
+ gcm-self-test
+
+gcm_self_test_CPPFLAGS = \
+ -DTESTDATADIR=\""$(top_srcdir)/plugins/color/test-data"\" \
+ $(AM_CPPFLAGS)
+
+gcm_self_test_CFLAGS = \
+ $(SETTINGS_PLUGIN_CFLAGS) \
+ $(COLORD_CFLAGS) \
+ $(PLUGIN_CFLAGS) \
+ $(AM_CFLAGS)
+
+gcm_self_test_SOURCES = \
+ gcm-tables.c \
+ gcm-tables.h \
+ gcm-dmi.c \
+ gcm-dmi.h \
+ gcm-edid.c \
+ gcm-edid.h \
+ gcm-self-test.c
+
+gcm_self_test_LDADD = \
+ $(COLORD_LIBS) \
+ $(LCMS_LIBS) \
+ $(SETTINGS_PLUGIN_LIBS)
+
+TESTS = gcm-self-test
+
+plugin_in_files = \
color.gnome-settings-plugin.in
plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin)
-EXTRA_DIST = \
+EXTRA_DIST = \
$(plugin_in_files)
-CLEANFILES = \
+CLEANFILES = \
$(plugin_DATA)
-DISTCLEANFILES = \
+DISTCLEANFILES = \
$(plugin_DATA)
@GSD_INTLTOOL_PLUGIN_RULE@
diff --git a/plugins/color/gcm-dmi.c b/plugins/color/gcm-dmi.c
new file mode 100644
index 0000000..f4be19a
--- /dev/null
+++ b/plugins/color/gcm-dmi.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <math.h>
+#include <string.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+
+#include "gcm-dmi.h"
+#include "gcm-tables.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) {
+ 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)
+{
+ 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};
+
+ 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
new file mode 100644
index 0000000..5aeb5b7
--- /dev/null
+++ b/plugins/color/gcm-dmi.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GCM_DMI_H
+#define __GCM_DMI_H
+
+#include <glib-object.h>
+
+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
new file mode 100644
index 0000000..55cb0cc
--- /dev/null
+++ b/plugins/color/gcm-edid.c
@@ -0,0 +1,457 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Soren Sandmann <sandmann redhat com>
+ * Copyright (C) 2009-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <math.h>
+#include <string.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+
+#include "gcm-edid.h"
+#include "gcm-tables.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;
+ GcmTables *tables;
+};
+
+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 = gcm_tables_get_pnp_id (priv->tables, priv->pnp_id, NULL);
+ 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 12 bytes, but we can't guarantee it's null
+ * terminated or not junk. */
+ text = g_strndup ((const gchar *) data, 12);
+
+ /* 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], 5, 4));
+
+ /* 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, 0x6c);
+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->tables = gcm_tables_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->tables);
+
+ 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
new file mode 100644
index 0000000..5eff73c
--- /dev/null
+++ b/plugins/color/gcm-edid.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GCM_EDID_H
+#define __GCM_EDID_H
+
+#include <glib-object.h>
+#include <colord.h>
+
+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-self-test.c b/plugins/color/gcm-self-test.c
new file mode 100644
index 0000000..073d090
--- /dev/null
+++ b/plugins/color/gcm-self-test.c
@@ -0,0 +1,145 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "gcm-edid.h"
+#include "gcm-dmi.h"
+#include "gcm-tables.h"
+
+static void
+gcm_test_tables_func (void)
+{
+ GcmTables *tables;
+ GError *error = NULL;
+ gchar *vendor;
+
+ tables = gcm_tables_new ();
+ g_assert (tables != NULL);
+
+ vendor = gcm_tables_get_pnp_id (tables, "IBM", &error);
+ g_assert_no_error (error);
+ g_assert (vendor != NULL);
+ g_assert_cmpstr (vendor, ==, "IBM France");
+ g_free (vendor);
+
+ vendor = gcm_tables_get_pnp_id (tables, "MIL", &error);
+ g_assert_no_error (error);
+ g_assert (vendor != NULL);
+ g_assert_cmpstr (vendor, ==, "Marconi Instruments Ltd");
+ g_free (vendor);
+
+ vendor = gcm_tables_get_pnp_id (tables, "XXX", &error);
+ g_assert_error (error, GCM_TABLES_ERROR, GCM_TABLES_ERROR_FAILED);
+ g_assert_cmpstr (vendor, ==, NULL);
+ g_error_free (error);
+
+ g_object_unref (tables);
+}
+
+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), ==, "80b7dda4c74b06366abb8fa23e71d645");
+ 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), ==, "c585d9e80adc65c54f0a52597e850f83");
+ 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)
+{
+ if (! g_thread_supported ())
+ g_thread_init (NULL);
+ gtk_init (&argc, &argv);
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/color/tables", gcm_test_tables_func);
+ 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/gcm-tables.c b/plugins/color/gcm-tables.c
new file mode 100644
index 0000000..998ae80
--- /dev/null
+++ b/plugins/color/gcm-tables.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gcm-tables.h"
+
+static void gcm_tables_finalize (GObject *object);
+
+#define GCM_TABLES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_TABLES, GcmTablesPrivate))
+
+struct _GcmTablesPrivate
+{
+ gchar *data_dir;
+ gchar *table_data;
+ GHashTable *pnp_table;
+};
+
+static gpointer gcm_tables_object = NULL;
+
+G_DEFINE_TYPE (GcmTables, gcm_tables, G_TYPE_OBJECT)
+
+GQuark
+gcm_tables_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("gcm_tables_error");
+ return quark;
+}
+
+static gboolean
+gcm_tables_load (GcmTables *tables, GError **error)
+{
+ gboolean ret;
+ gchar *filename = NULL;
+ gchar *retval = NULL;
+ GcmTablesPrivate *priv = tables->priv;
+ guint i;
+
+ /* load the contents */
+ g_debug ("loading: %s", PNPIDS_FILE);
+ ret = g_file_get_contents (PNPIDS_FILE, &priv->table_data, NULL, error);
+ if (!ret)
+ goto out;
+
+ /* parse into lines */
+ retval = priv->table_data;
+ for (i = 0; priv->table_data[i] != '\0'; i++) {
+
+ /* ignore */
+ if (priv->table_data[i] != '\n')
+ continue;
+
+ /* convert newline to NULL */
+ priv->table_data[i] = '\0';
+
+ /* the ID to text is a fixed offset */
+ retval[3] = '\0';
+ g_hash_table_insert (priv->pnp_table,
+ retval,
+ retval+4);
+ retval = &priv->table_data[i+1];
+ }
+out:
+ g_free (filename);
+ return ret;
+}
+
+gchar *
+gcm_tables_get_pnp_id (GcmTables *tables, const gchar *pnp_id, GError **error)
+{
+ gboolean ret;
+ gchar *retval = NULL;
+ GcmTablesPrivate *priv = tables->priv;
+ gpointer found;
+ guint size;
+
+ g_return_val_if_fail (GCM_IS_TABLES (tables), NULL);
+ g_return_val_if_fail (pnp_id != NULL, NULL);
+
+ /* if table is empty, try to load it */
+ size = g_hash_table_size (priv->pnp_table);
+ if (size == 0) {
+ ret = gcm_tables_load (tables, error);
+ if (!ret)
+ goto out;
+ }
+
+ /* look this up in the table */
+ found = g_hash_table_lookup (priv->pnp_table, pnp_id);
+ if (found == NULL) {
+ g_set_error (error,
+ GCM_TABLES_ERROR,
+ GCM_TABLES_ERROR_FAILED,
+ "could not find %s", pnp_id);
+ goto out;
+ }
+
+ /* return a copy */
+ retval = g_strdup (found);
+out:
+ return retval;
+}
+
+static void
+gcm_tables_class_init (GcmTablesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gcm_tables_finalize;
+ g_type_class_add_private (klass, sizeof (GcmTablesPrivate));
+}
+
+static void
+gcm_tables_init (GcmTables *tables)
+{
+ tables->priv = GCM_TABLES_GET_PRIVATE (tables);
+
+ /* we don't keep malloc'd data in the hash; instead we read it
+ * out into priv->table_data and then link to it in the hash */
+ tables->priv->pnp_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ NULL);
+}
+
+static void
+gcm_tables_finalize (GObject *object)
+{
+ GcmTables *tables = GCM_TABLES (object);
+ GcmTablesPrivate *priv = tables->priv;
+
+ g_free (priv->data_dir);
+ g_free (priv->table_data);
+ g_hash_table_unref (priv->pnp_table);
+
+ G_OBJECT_CLASS (gcm_tables_parent_class)->finalize (object);
+}
+
+GcmTables *
+gcm_tables_new (void)
+{
+ if (gcm_tables_object != NULL) {
+ g_object_ref (gcm_tables_object);
+ } else {
+ gcm_tables_object = g_object_new (GCM_TYPE_TABLES, NULL);
+ g_object_add_weak_pointer (gcm_tables_object, &gcm_tables_object);
+ }
+ return GCM_TABLES (gcm_tables_object);
+}
+
diff --git a/plugins/color/gcm-tables.h b/plugins/color/gcm-tables.h
new file mode 100644
index 0000000..7bc213c
--- /dev/null
+++ b/plugins/color/gcm-tables.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009-2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GCM_TABLES_H
+#define __GCM_TABLES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_TABLES (gcm_tables_get_type ())
+#define GCM_TABLES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_TABLES, GcmTables))
+#define GCM_TABLES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_TABLES, GcmTablesClass))
+#define GCM_IS_TABLES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_TABLES))
+#define GCM_IS_TABLES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_TABLES))
+#define GCM_TABLES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_TABLES, GcmTablesClass))
+#define GCM_TABLES_ERROR (gcm_tables_error_quark ())
+
+typedef struct _GcmTablesPrivate GcmTablesPrivate;
+typedef struct _GcmTables GcmTables;
+typedef struct _GcmTablesClass GcmTablesClass;
+
+struct _GcmTables
+{
+ GObject parent;
+ GcmTablesPrivate *priv;
+};
+
+struct _GcmTablesClass
+{
+ GObjectClass parent_class;
+};
+
+enum
+{
+ GCM_TABLES_ERROR_FAILED
+};
+
+GType gcm_tables_get_type (void);
+GQuark gcm_tables_error_quark (void);
+GcmTables *gcm_tables_new (void);
+gchar *gcm_tables_get_pnp_id (GcmTables *tables,
+ const gchar *pnp_id,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GCM_TABLES_H */
+
diff --git a/plugins/color/gsd-color-manager.c b/plugins/color/gsd-color-manager.c
index 3eb7da5..f8e22d7 100644
--- a/plugins/color/gsd-color-manager.c
+++ b/plugins/color/gsd-color-manager.c
@@ -25,6 +25,7 @@
#include <colord.h>
#include <libnotify/notify.h>
#include <gdk/gdk.h>
+#include <stdlib.h>
#ifdef HAVE_LIBCANBERRA
#include <canberra-gtk.h>
@@ -34,9 +35,14 @@
#include <lcms2.h>
#endif
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-rr.h>
+
#include "gnome-settings-profile.h"
#include "gsd-color-manager.h"
#include "gcm-profile-store.h"
+#include "gcm-dmi.h"
+#include "gcm-edid.h"
#define GSD_COLOR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_COLOR_MANAGER, GsdColorManagerPrivate))
@@ -50,6 +56,10 @@ struct GsdColorManagerPrivate
CdClient *client;
GSettings *settings;
GcmProfileStore *profile_store;
+ GcmDmi *dmi;
+ GnomeRRScreen *x11_screen;
+ GHashTable *edid_cache;
+ GdkWindow *gdk_window;
};
enum {
@@ -64,6 +74,1201 @@ G_DEFINE_TYPE (GsdColorManager, gsd_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
+gsd_color_manager_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("gsd_color_manager_error");
+ return quark;
+}
+
+static GcmEdid *
+gcm_session_get_output_edid (GsdColorManager *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 (GsdColorManager *manager,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean ret;
+ gchar *data = NULL;
+ gsize length;
+ guint version_data;
+ GsdColorManagerPrivate *priv = manager->priv;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ g_debug ("setting root window ICC profile atom from %s", filename);
+
+ /* get contents of file */
+ ret = g_file_get_contents (filename, &data, &length, 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);
+ return ret;
+}
+
+static gchar *
+gcm_session_get_output_id (GsdColorManager *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;
+ }
+
+ /* get EDID data */
+ vendor = gcm_edid_get_vendor_name (edid);
+ if (vendor != NULL)
+ g_string_append_printf (device_id, "-%s", vendor);
+ name = gcm_edid_get_monitor_name (edid);
+ if (name != NULL)
+ g_string_append_printf (device_id, "-%s", name);
+ serial = gcm_edid_get_serial_number (edid);
+ 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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "no connected output with that edid hash");
+ }
+out:
+ return output;
+}
+
+typedef struct {
+ GsdColorManager *manager;
+ CdProfile *profile;
+ CdDevice *device;
+ GnomeRROutput *output;
+} GcmSessionAsyncHelper;
+
+static void
+gcm_session_async_helper_free (GcmSessionAsyncHelper *helper)
+{
+ 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);
+}
+
+static void
+gcm_session_profile_assign_add_profile_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ 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;
+ }
+
+ /* 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);
+}
+
+static void
+gcm_session_profile_assign_device_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdDevice *device = CD_DEVICE (object);
+ gboolean ret;
+ GError *error = NULL;
+ GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
+
+ /* 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;
+ }
+
+ /* 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;
+}
+
+static void
+gcm_session_profile_assign_find_device_cb (GObject *object,
+ GAsyncResult *res,
+ 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 %s which should have been added: %s",
+ cd_device_get_id (device),
+ 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);
+}
+
+static void
+gcm_session_profile_assign_profile_connect_cb (GObject *object,
+ GAsyncResult *res,
+ 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;
+ GsdColorManager *manager = GSD_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);
+}
+
+static void
+gcm_session_profile_added_assign_cb (CdClient *client,
+ CdProfile *profile,
+ GsdColorManager *manager)
+{
+ cd_profile_connect (profile,
+ NULL,
+ gcm_session_profile_assign_profile_connect_cb,
+ manager);
+}
+
+static cmsBool
+_cmsWriteTagTextAscii (cmsHPROFILE lcms_profile,
+ cmsTagSignature sig,
+ const gchar *text)
+{
+ cmsBool ret;
+ cmsMLU *mlu = cmsMLUalloc (0, 1);
+ cmsMLUsetASCII (mlu, "EN", "us", text);
+ ret = cmsWriteTag (lcms_profile, sig, mlu);
+ cmsMLUfree (mlu);
+ return ret;
+}
+
+static gboolean
+gcm_utils_mkdir_for_filename (const gchar *filename, GError **error)
+{
+ 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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "could not get parent dir %s",
+ filename);
+ goto out;
+ }
+
+ /* ensure desination exists */
+ 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;
+}
+
+#ifdef HAVE_NEW_LCMS
+static cmsBool
+_cmsDictAddEntryAscii (cmsHANDLE dict,
+ const gchar *key,
+ const gchar *value)
+{
+ cmsBool ret;
+ wchar_t mb_key[1024];
+ wchar_t mb_value[1024];
+ mbstowcs (mb_key, key, sizeof (mb_key));
+ mbstowcs (mb_value, value, sizeof (mb_value));
+ ret = cmsDictAddEntry (dict, mb_key, mb_value, NULL, NULL);
+ return ret;
+}
+#endif /* HAVE_NEW_LCMS */
+
+static gboolean
+gcm_apply_create_icc_profile_for_edid (GsdColorManager *manager,
+ GcmEdid *edid,
+ const gchar *filename,
+ GError **error)
+{
+ const CdColorYxy *tmp;
+ cmsCIExyYTRIPLE chroma;
+ cmsCIExyY white_point;
+ cmsHPROFILE lcms_profile = NULL;
+ cmsToneCurve *transfer_curve[3];
+ const gchar *data;
+ gboolean ret = FALSE;
+ gchar *title = NULL;
+ gfloat localgamma;
+#ifdef HAVE_NEW_LCMS
+ cmsHANDLE dict = NULL;
+#endif
+ GsdColorManagerPrivate *priv = manager->priv;
+
+ /* ensure the per-user directory exists */
+ ret = gcm_utils_mkdir_for_filename (filename, error);
+ if (!ret)
+ goto out;
+
+ /* 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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to create profile");
+ goto out;
+ }
+
+ cmsSetColorSpace (lcms_profile, cmsSigRgbData);
+ cmsSetPCS (lcms_profile, cmsSigXYZData);
+ cmsSetHeaderRenderingIntent (lcms_profile,
+ INTENT_RELATIVE_COLORIMETRIC);
+ cmsSetDeviceClass (lcms_profile, cmsSigDisplayClass);
+
+ /* copyright */
+ ret = _cmsWriteTagTextAscii (lcms_profile,
+ cmsSigCopyrightTag,
+ "No copyright");
+ if (!ret) {
+ g_set_error_literal (error,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to write copyright");
+ goto out;
+ }
+
+ /* 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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to write model");
+ goto out;
+ }
+
+ /* write title */
+ title = g_strdup_printf ("%s, %s",
+ _("Default"),
+ data);
+ ret = _cmsWriteTagTextAscii (lcms_profile,
+ cmsSigProfileDescriptionTag,
+ title);
+ if (!ret) {
+ g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write description");
+ goto out;
+ }
+
+ /* 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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to write manufacturer");
+ goto out;
+ }
+
+#ifdef HAVE_NEW_LCMS
+ /* just create a new dict */
+ dict = cmsDictAlloc (NULL);
+
+ /* 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);
+#endif /* HAVE_NEW_LCMS */
+
+ /* write profile id */
+ ret = cmsMD5computeID (lcms_profile);
+ if (!ret) {
+ g_set_error_literal (error,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "failed to write profile id");
+ goto out;
+ }
+
+ /* save, TODO: get error */
+ cmsSaveProfileToFile (lcms_profile, filename);
+ ret = TRUE;
+out:
+ g_free (title);
+#ifdef HAVE_NEW_LCMS
+ if (dict != NULL)
+ cmsDictFree (dict);
+#endif
+ if (*transfer_curve != NULL)
+ cmsFreeToneCurve (*transfer_curve);
+ return ret;
+}
+
+static GPtrArray *
+gcm_session_generate_vcgt (CdProfile *profile, guint size)
+{
+ GnomeRROutputClutItem *tmp;
+ GPtrArray *array = NULL;
+ const cmsToneCurve **vcgt;
+ cmsFloat32Number in;
+ guint i;
+ const gchar *filename;
+ cmsHPROFILE lcms_profile = NULL;
+
+ /* not an actual profile */
+ filename = cd_profile_get_filename (profile);
+ if (filename == NULL)
+ goto out;
+
+ /* open file */
+ lcms_profile = cmsOpenProfileFromFile (filename, "r");
+ if (lcms_profile == NULL)
+ goto out;
+
+ /* get tone curves from profile */
+ vcgt = cmsReadTag (lcms_profile, cmsSigVcgtType);
+ if (vcgt == NULL || vcgt[0] == NULL) {
+ g_debug ("profile does not have any VCGT data");
+ goto out;
+ }
+
+ /* 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;
+}
+
+static guint
+gnome_rr_output_get_gamma_size (GnomeRROutput *output)
+{
+ GnomeRRCrtc *crtc;
+ gint len = 0;
+
+ crtc = gnome_rr_output_get_crtc (output);
+ gnome_rr_crtc_get_gamma (crtc,
+ &len,
+ NULL, NULL, NULL);
+ return (guint) len;
+}
+
+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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_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);
+ 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)
+{
+ gboolean ret;
+ GPtrArray *clut = NULL;
+
+ /* create a lookup table */
+ clut = gcm_session_generate_vcgt (profile,
+ gnome_rr_output_get_gamma_size (output));
+
+ /* 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;
+}
+
+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 = gnome_rr_output_get_gamma_size (output);
+ 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);
+ }
+
+ /* 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;
+}
+
+static GnomeRROutput *
+gcm_session_get_x11_output_by_id (GsdColorManager *manager,
+ const gchar *device_id,
+ GError **error)
+{
+ gchar *output_id;
+ GnomeRROutput *output = NULL;
+ GnomeRROutput **outputs = NULL;
+ guint i;
+ GsdColorManagerPrivate *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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_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,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
+ "Failed to find output %s",
+ device_id);
+ }
+out:
+ return output;
+}
+
+static void
+gcm_session_device_assign_profile_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CdProfile *profile = CD_PROFILE (object);
+ const gchar *filename;
+ gboolean ret;
+ GError *error = NULL;
+ GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
+ GsdColorManager *manager = GSD_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);
+
+ /* set the _ICC_PROFILE atom */
+ if (gnome_rr_output_get_is_primary (helper->output)) {
+ 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);
+ }
+ }
+
+ /* create a vcgt for this icc file */
+ ret = cd_profile_get_has_vcgt (profile);
+ if (ret) {
+ ret = gcm_session_device_set_gamma (helper->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 (helper->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;
+ }
+ }
+out:
+ gcm_session_async_helper_free (helper);
+}
+
+static void
+gcm_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;
+ GcmEdid *edid = NULL;
+ GnomeRROutput *output = NULL;
+ GError *error = NULL;
+ const gchar *xrandr_id;
+ GcmSessionAsyncHelper *helper;
+ CdDevice *device = CD_DEVICE (object);
+ GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
+ GsdColorManagerPrivate *priv = manager->priv;
+
+ /* get properties */
+ 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)
+ 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 = 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;
+ }
+
+ /* get the output EDID */
+ 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_error_free (error);
+ goto out;
+ }
+
+ /* create profile from device edid if it does not exist */
+ 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 = 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 (GsdColorManager *manager, CdDevice *device)
+{
+ cd_device_connect (device,
+ NULL,
+ gcm_session_device_assign_connect_cb,
+ manager);
+}
+
+static void
+gcm_session_device_added_assign_cb (CdClient *client,
+ CdDevice *device,
+ GsdColorManager *manager)
+{
+ gcm_session_device_assign (manager, device);
+}
+
+static void
+gcm_session_device_changed_assign_cb (CdClient *client,
+ CdDevice *device,
+ GsdColorManager *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) {
+ 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 (GsdColorManager *manager, GnomeRROutput *output)
+{
+ const gchar *model;
+ const gchar *serial;
+ const gchar *vendor;
+ gboolean ret;
+ gchar *device_id = NULL;
+ GcmEdid *edid;
+ GError *error = NULL;
+ GHashTable *device_props = NULL;
+ GsdColorManagerPrivate *priv = manager->priv;
+
+ /* get edid */
+ edid = gcm_session_get_output_edid (manager, output, &error);
+ if (edid == NULL) {
+ g_warning ("failed to get edid: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* is this an internal device? */
+ ret = gnome_rr_output_is_laptop (output);
+ if (ret) {
+ model = gcm_dmi_get_name (priv->dmi);
+ vendor = gcm_dmi_get_vendor (priv->dmi);
+ } else {
+ model = gcm_edid_get_monitor_name (edid);
+ if (model == NULL)
+ model = gnome_rr_output_get_name (output);
+ vendor = gcm_edid_get_vendor_name (edid);
+ }
+
+ /* get a serial number if one exists */
+ serial = gcm_edid_get_serial_number (edid);
+ 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));
+ cd_client_create_device (priv->client,
+ device_id,
+ CD_OBJECT_SCOPE_TEMP,
+ device_props,
+ NULL,
+ gcm_session_create_device_cb,
+ manager);
+out:
+ 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,
+ GsdColorManager *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;
+ GsdColorManager *manager = GSD_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
+gnome_rr_screen_output_removed_cb (GnomeRRScreen *screen,
+ GnomeRROutput *output,
+ GsdColorManager *manager)
+{
+ g_debug ("output %s removed",
+ gnome_rr_output_get_name (output));
+ cd_client_find_device (manager->priv->client,
+ 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;
+ GsdColorManager *manager = GSD_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_client_connect_cb (GObject *source_object,
GAsyncResult *res,
@@ -71,6 +1276,8 @@ gcm_session_client_connect_cb (GObject *source_object,
{
gboolean ret;
GError *error = NULL;
+ GnomeRROutput **outputs;
+ guint i;
GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
GsdColorManagerPrivate *priv = manager->priv;
@@ -85,6 +1292,50 @@ gcm_session_client_connect_cb (GObject *source_object,
/* 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 (gnome_rr_screen_output_added_cb),
+ manager);
+ g_signal_connect (priv->x11_screen, "output-disconnected",
+ G_CALLBACK (gnome_rr_screen_output_removed_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
@@ -92,17 +1343,26 @@ gsd_color_manager_start (GsdColorManager *manager,
GError **error)
{
GsdColorManagerPrivate *priv = manager->priv;
+ gboolean ret = FALSE;
g_debug ("Starting color manager");
gnome_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:
gnome_settings_profile_end (NULL);
- return TRUE;
+ return ret;
}
void
@@ -271,11 +1531,6 @@ gcm_session_notify_device (GsdColorManager *manager, CdDevice *device)
g_free (message);
}
-typedef struct {
- GsdColorManager *manager;
- CdDevice *device;
-} GsdColorManagerDeviceHelper;
-
static void
gcm_session_profile_connect_cb (GObject *object,
GAsyncResult *res,
@@ -287,7 +1542,7 @@ gcm_session_profile_connect_cb (GObject *object,
gchar *basename = NULL;
GError *error = NULL;
CdProfile *profile = CD_PROFILE (object);
- GsdColorManagerDeviceHelper *helper = (GsdColorManagerDeviceHelper *) user_data;
+ GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data;
GsdColorManager *manager = GSD_COLOR_MANAGER (helper->manager);
ret = cd_profile_connect_finish (profile,
@@ -320,9 +1575,7 @@ gcm_session_profile_connect_cb (GObject *object,
/* handle device */
gcm_session_notify_device (manager, helper->device);
out:
- g_object_unref (helper->device);
- g_object_unref (helper->manager);
- g_free (helper);
+ gcm_session_async_helper_free (helper);
g_free (basename);
}
@@ -337,7 +1590,7 @@ gcm_session_device_connect_cb (GObject *object,
CdProfile *profile = NULL;
CdDevice *device = CD_DEVICE (object);
GsdColorManager *manager = GSD_COLOR_MANAGER (user_data);
- GsdColorManagerDeviceHelper *helper;
+ GcmSessionAsyncHelper *helper;
ret = cd_device_connect_finish (device,
res,
@@ -353,7 +1606,7 @@ gcm_session_device_connect_cb (GObject *object,
kind = cd_device_get_kind (device);
if (kind != CD_DEVICE_KIND_DISPLAY &&
kind != CD_DEVICE_KIND_PRINTER)
- return;
+ goto out;
/* ensure we have a profile */
profile = cd_device_get_default_profile (device);
@@ -363,7 +1616,7 @@ gcm_session_device_connect_cb (GObject *object,
}
/* connect to the profile */
- helper = g_new0 (GsdColorManagerDeviceHelper, 1);
+ helper = g_new0 (GcmSessionAsyncHelper, 1);
helper->manager = g_object_ref (manager);
helper->device = g_object_ref (device);
cd_profile_connect (profile,
@@ -398,7 +1651,7 @@ gcm_session_get_precooked_md5 (cmsHPROFILE lcms_profile)
/* check to see if we have a pre-cooked MD5 */
cmsGetHeaderProfileID (lcms_profile, profile_id);
- for (i=0; i<16; i++) {
+ for (i = 0; i < 16; i++) {
if (profile_id[i] != 0) {
md5_precooked = TRUE;
break;
@@ -409,7 +1662,7 @@ gcm_session_get_precooked_md5 (cmsHPROFILE lcms_profile)
/* convert to a hex string */
md5 = g_new0 (gchar, 32 + 1);
- for (i=0; i<16; i++)
+ for (i = 0; i < 16; i++)
g_snprintf (md5 + i*2, 3, "%02x", profile_id[i]);
out:
return md5;
@@ -431,7 +1684,9 @@ gcm_session_get_md5_for_filename (const gchar *filename,
/* get the internal profile id, if it exists */
lcms_profile = cmsOpenProfileFromFile (filename, "r");
if (lcms_profile == NULL) {
- g_set_error_literal (error, 1, 0,
+ g_set_error_literal (error,
+ GSD_COLOR_MANAGER_ERROR,
+ GSD_COLOR_MANAGER_ERROR_FAILED,
"failed to load: not an ICC profile");
goto out;
}
@@ -664,6 +1919,18 @@ gsd_color_manager_init (GsdColorManager *manager)
GsdColorManagerPrivate *priv;
priv = manager->priv = GSD_COLOR_MANAGER_GET_PRIVATE (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);
+
+ /* use DMI data for internal panels */
+ priv->dmi = gcm_dmi_new ();
+
priv->settings = g_settings_new ("org.gnome.settings-daemon.plugins.color");
priv->client = cd_client_new ();
g_signal_connect (priv->client, "device-added",
@@ -701,6 +1968,10 @@ gsd_color_manager_finalize (GObject *object)
g_object_unref (manager->priv->settings);
g_object_unref (manager->priv->client);
g_object_unref (manager->priv->profile_store);
+ g_object_unref (manager->priv->dmi);
+ g_hash_table_destroy (manager->priv->edid_cache);
+ if (manager->priv->x11_screen != NULL);
+ g_object_unref (manager->priv->x11_screen);
G_OBJECT_CLASS (gsd_color_manager_parent_class)->finalize (object);
}
diff --git a/plugins/color/gsd-color-manager.h b/plugins/color/gsd-color-manager.h
index 5180f3b..27321a7 100644
--- a/plugins/color/gsd-color-manager.h
+++ b/plugins/color/gsd-color-manager.h
@@ -32,6 +32,7 @@ G_BEGIN_DECLS
#define GSD_IS_COLOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_COLOR_MANAGER))
#define GSD_IS_COLOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_COLOR_MANAGER))
#define GSD_COLOR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_COLOR_MANAGER, GsdColorManagerClass))
+#define GSD_COLOR_MANAGER_ERROR (gsd_color_manager_error_quark ())
typedef struct GsdColorManagerPrivate GsdColorManagerPrivate;
@@ -46,7 +47,13 @@ typedef struct
GObjectClass parent_class;
} GsdColorManagerClass;
+enum
+{
+ GSD_COLOR_MANAGER_ERROR_FAILED
+};
+
GType gsd_color_manager_get_type (void);
+GQuark gsd_color_manager_error_quark (void);
GsdColorManager * gsd_color_manager_new (void);
gboolean gsd_color_manager_start (GsdColorManager *manager,
diff --git a/plugins/color/test-data/LG-L225W-External.bin b/plugins/color/test-data/LG-L225W-External.bin
new file mode 100644
index 0000000..f08310a
Binary files /dev/null and b/plugins/color/test-data/LG-L225W-External.bin differ
diff --git a/plugins/color/test-data/Lenovo-T61-Internal.bin b/plugins/color/test-data/Lenovo-T61-Internal.bin
new file mode 100644
index 0000000..45aec9d
Binary files /dev/null and b/plugins/color/test-data/Lenovo-T61-Internal.bin differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]