[gnome-color-manager] Refactor the GcmClut object so it's based on an abstract GcmDevice, with GcmClient container



commit 7dfb253cbb07be74a4b4cec0294ddf442546e9af
Author: Richard Hughes <richard hughsie com>
Date:   Wed Nov 4 22:04:05 2009 +0000

    Refactor the GcmClut object so it's based on an abstract GcmDevice, with GcmClient container
    
    This lets us do sexy things with scanners and printers in the future.
    It might introduce a regression or two, but this can be easily fixed

 src/Makefile.am     |    8 +
 src/gcm-apply.c     |   41 +++--
 src/gcm-calibrate.c |    1 +
 src/gcm-client.c    |  585 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gcm-client.h    |   73 +++++++
 src/gcm-clut.c      |  148 +-------------
 src/gcm-clut.h      |    3 -
 src/gcm-device.c    |  516 +++++++++++++++++++++++++++++++++++++++++++++
 src/gcm-device.h    |   76 +++++++
 src/gcm-inspect.c   |    1 +
 src/gcm-prefs.c     |  360 +++++++++++++++----------------
 src/gcm-utils.c     |  187 ++++++-----------
 src/gcm-utils.h     |   17 +--
 13 files changed, 1527 insertions(+), 489 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c474156..f4ad7c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,7 @@ INCLUDES =						\
 	$(VTE_CFLAGS)					\
 	$(DBUS_GLIB_CFLAGS)				\
 	-DG_UDEV_API_IS_SUBJECT_TO_CHANGE		\
+	-DGNOME_DESKTOP_USE_UNSTABLE_API		\
 	$(GUDEV_CFLAGS)					\
 	-DBINDIR=\"$(bindir)\"			 	\
 	-DSYSCONFDIR=\""$(sysconfdir)"\" 		\
@@ -30,6 +31,10 @@ libgcmshared_a_SOURCES =				\
 	gcm-edid.h					\
 	gcm-xserver.c					\
 	gcm-xserver.h					\
+	gcm-client.c					\
+	gcm-client.h					\
+	gcm-device.c					\
+	gcm-device.h					\
 	gcm-profile.c					\
 	gcm-profile.h
 
@@ -52,6 +57,7 @@ gcm_inspect_LDADD =					\
 	$(GLIB_LIBS)					\
 	$(GNOMEDESKTOP_LIBS)				\
 	$(GCONF_LIBS)					\
+	$(GUDEV_LIBS)					\
 	$(GTK_LIBS)
 
 gcm_inspect_CFLAGS =					\
@@ -65,6 +71,7 @@ gcm_apply_LDADD =					\
 	$(GLIB_LIBS)					\
 	$(GNOMEDESKTOP_LIBS)				\
 	$(GCONF_LIBS)					\
+	$(GUDEV_LIBS)					\
 	$(GTK_LIBS)
 
 gcm_apply_CFLAGS =					\
@@ -78,6 +85,7 @@ gcm_import_LDADD =					\
 	$(GLIB_LIBS)					\
 	$(GNOMEDESKTOP_LIBS)				\
 	$(GCONF_LIBS)					\
+	$(GUDEV_LIBS)					\
 	$(GTK_LIBS)
 
 gcm_import_CFLAGS =					\
diff --git a/src/gcm-apply.c b/src/gcm-apply.c
index 35f3abf..3c2b8b6 100644
--- a/src/gcm-apply.c
+++ b/src/gcm-apply.c
@@ -26,6 +26,7 @@
 #include "egg-debug.h"
 
 #include "gcm-utils.h"
+#include "gcm-client.h"
 
 /**
  * main:
@@ -38,10 +39,11 @@ main (int argc, char **argv)
 	guint retval = 0;
 	GError *error = NULL;
 	GOptionContext *context;
-	GnomeRROutput **outputs;
+	GPtrArray *array = NULL;
 	guint i;
-	GnomeRRScreen *rr_screen = NULL;
-	gboolean connected;
+	GcmClient *client = NULL;
+	GcmDevice *device;
+	GcmDeviceType type;
 
 	const GOptionEntry options[] = {
 		{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
@@ -58,22 +60,29 @@ main (int argc, char **argv)
 
 	egg_debug_init (verbose);
 
-	/* get screen */
-	rr_screen = gnome_rr_screen_new (gdk_screen_get_default (), NULL, NULL, &error);
-	if (rr_screen == NULL) {
-		egg_warning ("failed to get rr screen: %s", error->message);
+	/* get devices */
+	client = gcm_client_new ();
+	ret = gcm_client_coldplug (client, &error);
+	if (!ret) {
+		egg_warning ("failed to get devices: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 
 	/* set for each output */
-	outputs = gnome_rr_screen_list_outputs (rr_screen);
-	for (i=0; outputs[i] != NULL; i++) {
-		/* if nothing connected then ignore */
-		connected = gnome_rr_output_is_connected (outputs[i]);
-		if (!connected)
+	array = gcm_client_get_devices (client);
+	for (i=0; i<array->len; i++) {
+		device = g_ptr_array_index (array, i);
+		g_object_get (device,
+			      "type", &type,
+			      NULL);
+
+		/* not a xrandr panel */
+		if (type != GCM_DEVICE_TYPE_DISPLAY)
 			continue;
-		ret = gcm_utils_set_output_gamma (outputs[i], &error);
+
+		/* set gamma for device */
+		ret = gcm_utils_set_gamma_for_device (device, &error);
 		if (!ret) {
 			retval = 1;
 			egg_warning ("failed to set gamma: %s", error->message);
@@ -82,8 +91,10 @@ main (int argc, char **argv)
 		}
 	}
 out:
-	if (rr_screen != NULL)
-		gnome_rr_screen_destroy (rr_screen);
+	if (array != NULL)
+		g_ptr_array_unref (array);
+	if (client != NULL)
+		g_object_unref (client);
 	return retval;
 }
 
diff --git a/src/gcm-calibrate.c b/src/gcm-calibrate.c
index fd15250..5b48d42 100644
--- a/src/gcm-calibrate.c
+++ b/src/gcm-calibrate.c
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <gtk/gtk.h>
 #include <vte/vte.h>
+#include <libgnomeui/gnome-rr.h>
 
 #include "gcm-calibrate.h"
 #include "gcm-edid.h"
diff --git a/src/gcm-client.c b/src/gcm-client.c
new file mode 100644
index 0000000..dbbb335
--- /dev/null
+++ b/src/gcm-client.c
@@ -0,0 +1,585 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 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 client.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gcm-client
+ * @short_description: Client object to hold an array of devices.
+ *
+ * This object holds an array of %GcmDevices, and watches both udev and xorg
+ * for changes. If a device is added or removed then a signal is fired.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <gudev/gudev.h>
+#include <libgnomeui/gnome-rr.h>
+
+#include "gcm-client.h"
+#include "gcm-utils.h"
+#include "gcm-edid.h"
+
+#include "egg-debug.h"
+
+static void     gcm_client_finalize	(GObject     *object);
+
+#define GCM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_CLIENT, GcmClientPrivate))
+
+/**
+ * GcmClientPrivate:
+ *
+ * Private #GcmClient data
+ **/
+struct _GcmClientPrivate
+{
+	gchar				*display_name;
+	GPtrArray			*array;
+	GUdevClient			*gudev_client;
+	GnomeRRScreen			*rr_screen;
+};
+
+enum {
+	PROP_0,
+	PROP_DISPLAY_NAME,
+	PROP_LAST
+};
+
+enum {
+	SIGNAL_ADDED,
+	SIGNAL_REMOVED,
+	SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+G_DEFINE_TYPE (GcmClient, gcm_client, G_TYPE_OBJECT)
+
+/**
+ * gcm_client_get_devices:
+ *
+ * @client: a valid %GcmClient instance
+ *
+ * Gets the device list.
+ *
+ * Return value: an array, free with g_ptr_array_unref()
+ **/
+GPtrArray *
+gcm_client_get_devices (GcmClient *client)
+{
+	g_return_val_if_fail (GCM_IS_CLIENT (client), NULL);
+	return g_ptr_array_ref (client->priv->array);
+}
+
+/**
+ * gcm_client_get_device_by_id:
+ *
+ * @client: a valid %GcmClient instance
+ * @id: the device identifer
+ *
+ * Gets a device.
+ *
+ * Return value: a valid %GcmDevice or %NULL. Free with g_object_unref()
+ **/
+GcmDevice *
+gcm_client_get_device_by_id (GcmClient *client, const gchar *id)
+{
+	guint i;
+	GcmDevice *device = NULL;
+	GcmDevice *device_tmp;
+	const gchar *id_tmp;
+	GcmClientPrivate *priv = client->priv;
+
+	g_return_val_if_fail (GCM_IS_CLIENT (client), NULL);
+	g_return_val_if_fail (id != NULL, NULL);
+
+	/* find device */
+	for (i=0; i<priv->array->len; i++) {
+		device_tmp = g_ptr_array_index (priv->array, i);
+		id_tmp = gcm_device_get_id (device_tmp);
+		if (g_strcmp0 (id, id_tmp) == 0) {
+			device = g_object_ref (device_tmp);
+			break;
+		}
+	}
+
+	return device;
+}
+
+/**
+ * gcm_client_get_id_for_udev_device:
+ **/
+static gchar *
+gcm_client_get_id_for_udev_device (GUdevDevice *udev_device)
+{
+	gchar *id;
+	guint i;
+
+	/* get id */
+	id = g_strdup_printf ("sysfs_%s_%s",
+			      g_udev_device_get_property (udev_device, "ID_VENDOR"),
+			      g_udev_device_get_property (udev_device, "ID_MODEL"));
+
+	/* replace unsafe chars */
+	for (i=0; id[i] != '\0'; i++) {
+		if (!g_ascii_isalnum (id[i]))
+			id[i] = '_';
+	}
+	return id;
+}
+
+/**
+ * gcm_client_gudev_remove:
+ **/
+static void
+gcm_client_gudev_remove (GcmClient *client, GUdevDevice *udev_device)
+{
+	GcmDevice *device;
+	gchar *id;
+	GcmClientPrivate *priv = client->priv;
+
+	/* generate id */
+	id = gcm_client_get_id_for_udev_device (udev_device);
+	device = gcm_client_get_device_by_id (client, id);
+	if (device != NULL) {
+		/* emit before we remove so the device is valid */
+		egg_debug ("emit removed: %s", id);
+		g_signal_emit (client, signals[SIGNAL_REMOVED], 0, device);
+		g_ptr_array_remove (priv->array, device);
+		g_object_unref (device);
+	}
+	g_free (id);
+}
+
+/**
+ * gcm_client_gudev_add_scanner:
+ **/
+static void
+gcm_client_gudev_add_scanner (GcmClient *client, GUdevDevice *udev_device)
+{
+	gchar *title;
+	GcmDevice *device;
+	gchar *id;
+	GcmClientPrivate *priv = client->priv;
+
+	/* add new device */
+	device = gcm_device_new ();
+	id = gcm_client_get_id_for_udev_device (udev_device);
+	title = g_strdup_printf ("%s - %s",
+				g_udev_device_get_property (udev_device, "ID_VENDOR"),
+				g_udev_device_get_property (udev_device, "ID_MODEL"));
+	g_object_set (device,
+		      "type", GCM_DEVICE_TYPE_SCANNER,
+		      "id", id,
+		      "title", title,
+		      NULL);
+	g_ptr_array_add (priv->array, device);
+
+	/* signal the addition */
+	egg_debug ("emit: added %s to device list", id);
+	g_signal_emit (client, signals[SIGNAL_ADDED], 0, device);
+
+	/* add to list */
+	g_free (id);
+	g_free (title);
+}
+
+/**
+ * gcm_client_gudev_add:
+ **/
+static void
+gcm_client_gudev_add (GcmClient *client, GUdevDevice *udev_device)
+{
+	const gchar *value;
+	/* sane is slightly odd in a lowercase property, and "yes" as a value rather than "1" */
+	value = g_udev_device_get_property (udev_device, "libsane_matched");
+	if (value != NULL) {
+		egg_debug ("found scanner device: %s", g_udev_device_get_sysfs_path (udev_device));
+		gcm_client_gudev_add_scanner (client, udev_device);
+	}
+}
+
+/**
+ * gcm_client_uevent_cb:
+ **/
+static void
+gcm_client_uevent_cb (GUdevClient *gudev_client, const gchar *action, GUdevDevice *udev_device, GcmClient *client)
+{
+	if (g_strcmp0 (action, "remove") == 0)
+		gcm_client_gudev_remove (client, udev_device);
+	else if (g_strcmp0 (action, "add") == 0)
+		gcm_client_gudev_add (client, udev_device);
+}
+
+/**
+ * gcm_client_coldplug_devices_usb:
+ **/
+static gboolean
+gcm_client_coldplug_devices_usb (GcmClient *client, GError **error)
+{
+	GList *devices;
+	GList *l;
+	GUdevDevice *udev_device;
+	GcmClientPrivate *priv = client->priv;
+
+	/* get all USB devices */
+	devices = g_udev_client_query_by_subsystem (priv->gudev_client, "usb");
+	for (l = devices; l != NULL; l = l->next) {
+		udev_device = l->data;
+		gcm_client_gudev_add (client, udev_device);
+	}
+
+	g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+	g_list_free (devices);
+	return TRUE;
+}
+
+/**
+ * gcm_utils_get_output_name:
+ *
+ * Return value: the output name, free with g_free().
+ **/
+static gchar *
+gcm_utils_get_output_name (GnomeRROutput *output)
+{
+	const guint8 *data;
+	const gchar *output_name;
+	gchar *name = NULL;
+	GcmEdid *edid = NULL;
+	gboolean ret;
+
+	/* if nothing connected then fall back to the connector name */
+	ret = gnome_rr_output_is_connected (output);
+	if (!ret)
+		goto out;
+
+	/* parse the EDID to get a crtc-specific name, not an output specific name */
+	data = gnome_rr_output_get_edid_data (output);
+	if (data == NULL)
+		goto out;
+
+	edid = gcm_edid_new ();
+	ret = gcm_edid_parse (edid, data, NULL);
+	if (!ret) {
+		egg_warning ("failed to parse edid");
+		goto out;
+	}
+
+	/* find the best option */
+	g_object_get (edid, "monitor-name", &name, NULL);
+	if (name == NULL)
+		g_object_get (edid, "ascii-string", &name, NULL);
+	if (name == NULL)
+		g_object_get (edid, "serial-number", &name, NULL);
+
+out:
+	/* fallback to the output name */
+	if (name == NULL) {
+		output_name = gnome_rr_output_get_name (output);
+		ret = gcm_utils_output_is_lcd_internal (output_name);
+		if (ret)
+			output_name = "Internal LCD";
+		name = g_strdup (output_name);
+	}
+
+	if (edid != NULL)
+		g_object_unref (edid);
+	return name;
+}
+
+/**
+ * gcm_client_get_id_for_xrandr_device:
+ **/
+static gchar *
+gcm_client_get_id_for_xrandr_device (GnomeRROutput *output)
+{
+	gchar *id;
+	gchar *name;
+	guint i;
+
+	/* get id */
+	name = gcm_utils_get_output_name (output);
+	id = g_strdup_printf ("xrandr_%s", name);
+
+	/* replace unsafe chars */
+	for (i=0; id[i] != '\0'; i++) {
+		if (!g_ascii_isalnum (id[i]))
+			id[i] = '_';
+	}
+	g_free (name);
+	return id;
+}
+
+/**
+ * gcm_client_xrandr_add:
+ **/
+static void
+gcm_client_xrandr_add (GcmClient *client, GnomeRROutput *output)
+{
+	gchar *title = NULL;
+	gchar *id;
+	gboolean ret;
+	GcmDevice *device = NULL;
+	GError *error = NULL;
+	GcmClientPrivate *priv = client->priv;
+
+	/* get details */
+	id = gcm_client_get_id_for_xrandr_device (output);
+
+	/* if nothing connected then ignore */
+	ret = gnome_rr_output_is_connected (output);
+	if (!ret) {
+		egg_debug ("%s is not connected", id);
+		goto out;
+	}
+
+	/* are we already in the list */
+	device = gcm_client_get_device_by_id (client, id);
+	if (device != NULL) {
+		egg_debug ("%s already added", id);
+		g_object_unref (device);
+		goto out;
+	}
+
+	/* add new device */
+	device = gcm_device_new ();
+	title = gcm_utils_get_output_name (output);
+	g_object_set (device,
+		      "type", GCM_DEVICE_TYPE_DISPLAY,
+		      "id", id,
+		      "title", title,
+		      "native-device-xrandr", output,
+		      NULL);
+
+	/* load the device */
+	ret = gcm_device_load (device, &error);
+	if (!ret) {
+		egg_warning ("failed to load: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* add to the array */
+	g_ptr_array_add (priv->array, g_object_ref (device));
+
+	/* signal the addition */
+	egg_debug ("emit: added %s to device list", id);
+	g_signal_emit (client, signals[SIGNAL_ADDED], 0, device);
+out:
+	if (device != NULL)
+		g_object_unref (device);
+	g_free (id);
+	g_free (title);
+}
+
+/**
+ * gcm_client_randr_event_cb:
+ **/
+static void
+gcm_client_randr_event_cb (GnomeRRScreen *screen, GcmClient *client)
+{
+	GnomeRROutput **outputs;
+	guint i;
+
+	egg_debug ("screens may have changed");
+
+	/* replug devices */
+	outputs = gnome_rr_screen_list_outputs (client->priv->rr_screen);
+	for (i=0; outputs[i] != NULL; i++)
+		gcm_client_xrandr_add (client, outputs[i]);
+}
+
+/**
+ * gcm_client_coldplug_devices_xrandr:
+ **/
+static gboolean
+gcm_client_coldplug_devices_xrandr (GcmClient *client, GError **error)
+{
+	GnomeRROutput **outputs;
+	guint i;
+	GcmClientPrivate *priv = client->priv;
+
+	outputs = gnome_rr_screen_list_outputs (priv->rr_screen);
+	for (i=0; outputs[i] != NULL; i++)
+		gcm_client_xrandr_add (client, outputs[i]);
+	return TRUE;
+}
+
+/**
+ * gcm_client_coldplug:
+ **/
+gboolean
+gcm_client_coldplug (GcmClient *client, GError **error)
+{
+	gboolean ret;
+
+	g_return_val_if_fail (GCM_IS_CLIENT (client), FALSE);
+
+	/* usb */
+	ret = gcm_client_coldplug_devices_usb (client, error);
+	if (!ret)
+		goto out;
+
+	/* xorg */
+	ret = gcm_client_coldplug_devices_xrandr (client, error);
+	if (!ret)
+		goto out;
+out:
+	return ret;
+}
+
+/**
+ * gcm_client_get_property:
+ **/
+static void
+gcm_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	GcmClient *client = GCM_CLIENT (object);
+	GcmClientPrivate *priv = client->priv;
+
+	switch (prop_id) {
+	case PROP_DISPLAY_NAME:
+		g_value_set_string (value, priv->display_name);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_client_set_property:
+ **/
+static void
+gcm_client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	GcmClient *client = GCM_CLIENT (object);
+	GcmClientPrivate *priv = client->priv;
+
+	switch (prop_id) {
+	case PROP_DISPLAY_NAME:
+		g_free (priv->display_name);
+		priv->display_name = g_strdup (g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_client_class_init:
+ **/
+static void
+gcm_client_class_init (GcmClientClass *klass)
+{
+	GParamSpec *pspec;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gcm_client_finalize;
+	object_class->get_property = gcm_client_get_property;
+	object_class->set_property = gcm_client_set_property;
+
+	/**
+	 * GcmClient:display-name:
+	 */
+	pspec = g_param_spec_string ("display-name", NULL, NULL,
+				     NULL,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_DISPLAY_NAME, pspec);
+
+	/**
+	 * GcmClient::added
+	 **/
+	signals[SIGNAL_ADDED] =
+		g_signal_new ("added",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GcmClientClass, added),
+			      NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
+			      G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	/**
+	 * GcmClient::removed
+	 **/
+	signals[SIGNAL_REMOVED] =
+		g_signal_new ("removed",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GcmClientClass, removed),
+			      NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
+			      G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	g_type_class_add_private (klass, sizeof (GcmClientPrivate));
+}
+
+/**
+ * gcm_client_init:
+ **/
+static void
+gcm_client_init (GcmClient *client)
+{
+	GError *error = NULL;
+	const gchar *subsystems[] = {"usb", NULL};
+
+	client->priv = GCM_CLIENT_GET_PRIVATE (client);
+	client->priv->display_name = NULL;
+	client->priv->array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+
+	/* use GUdev to find devices */
+	client->priv->gudev_client = g_udev_client_new (subsystems);
+	g_signal_connect (client->priv->gudev_client, "uevent",
+			  G_CALLBACK (gcm_client_uevent_cb), client);
+
+	/* get screen */
+	client->priv->rr_screen = gnome_rr_screen_new (gdk_screen_get_default (), (GnomeRRScreenChanged) gcm_client_randr_event_cb, client, &error);
+	if (client->priv->rr_screen == NULL) {
+		egg_error ("failed to get rr screen: %s", error->message);
+		g_error_free (error);
+	}
+}
+
+/**
+ * gcm_client_finalize:
+ **/
+static void
+gcm_client_finalize (GObject *object)
+{
+	GcmClient *client = GCM_CLIENT (object);
+	GcmClientPrivate *priv = client->priv;
+
+	g_free (priv->display_name);
+	g_ptr_array_unref (priv->array);
+	g_object_unref (priv->gudev_client);
+	gnome_rr_screen_destroy (priv->rr_screen);
+
+	G_OBJECT_CLASS (gcm_client_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_client_new:
+ *
+ * Return value: a new GcmClient object.
+ **/
+GcmClient *
+gcm_client_new (void)
+{
+	GcmClient *client;
+	client = g_object_new (GCM_TYPE_CLIENT, NULL);
+	return GCM_CLIENT (client);
+}
+
diff --git a/src/gcm-client.h b/src/gcm-client.h
new file mode 100644
index 0000000..82dd1f1
--- /dev/null
+++ b/src/gcm-client.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 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 client.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GCM_CLIENT_H
+#define __GCM_CLIENT_H
+
+#include <glib-object.h>
+
+#include "gcm-device.h"
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_CLIENT			(gcm_client_get_type ())
+#define GCM_CLIENT(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_CLIENT, GcmClient))
+#define GCM_CLIENT_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_CLIENT, GcmClientClass))
+#define GCM_IS_CLIENT(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_CLIENT))
+#define GCM_IS_CLIENT_CLASS(k)		(G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_CLIENT))
+#define GCM_CLIENT_GET_CLASS(o)		(G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_CLIENT, GcmClientClass))
+
+typedef struct _GcmClientPrivate	GcmClientPrivate;
+typedef struct _GcmClient		GcmClient;
+typedef struct _GcmClientClass		GcmClientClass;
+
+struct _GcmClient
+{
+	 GObject			 parent;
+	 GcmClientPrivate		*priv;
+};
+
+struct _GcmClientClass
+{
+	GObjectClass	parent_class;
+	void		(* added)				(GcmDevice	*device);
+	void		(* removed)				(GcmDevice	*device);
+	/* padding for future expansion */
+	void (*_gcm_reserved1) (void);
+	void (*_gcm_reserved2) (void);
+	void (*_gcm_reserved3) (void);
+	void (*_gcm_reserved4) (void);
+	void (*_gcm_reserved5) (void);
+};
+
+GType		 gcm_client_get_type		  		(void);
+GcmClient	*gcm_client_new					(void);
+
+GcmDevice	*gcm_client_get_device_by_id			(GcmClient		*client,
+								 const gchar		*id);
+gboolean	 gcm_client_coldplug				(GcmClient		*client,
+								 GError			**error);
+GPtrArray	*gcm_client_get_devices				(GcmClient		*client);
+
+G_END_DECLS
+
+#endif /* __GCM_CLIENT_H */
+
diff --git a/src/gcm-clut.c b/src/gcm-clut.c
index 96b2c3c..8345aea 100644
--- a/src/gcm-clut.c
+++ b/src/gcm-clut.c
@@ -80,7 +80,7 @@ G_DEFINE_TYPE (GcmClut, gcm_clut, G_TYPE_OBJECT)
 /**
  * gcm_clut_set_from_data:
  **/
-gboolean
+static gboolean
 gcm_clut_set_from_data (GcmClut *clut, const GcmClutData *data, guint size)
 {
 	guint i;
@@ -116,6 +116,7 @@ gcm_clut_load_from_profile (GcmClut *clut, GError **error)
 
 	/* no profile to load */
 	if (clut->priv->profile == NULL) {
+		egg_debug ("no profile to load");
 		g_free (clut->priv->copyright);
 		g_free (clut->priv->description);
 		clut->priv->copyright = NULL;
@@ -156,151 +157,6 @@ out:
 }
 
 /**
- * gcm_clut_get_default_config_location:
- **/
-static gchar *
-gcm_clut_get_default_config_location (void)
-{
-	gchar *filename;
-
-	/* create default path */
-	filename = g_build_filename (g_get_user_config_dir (), "gnome-color-manager", "config.dat", NULL);
-
-	return filename;
-}
-
-/**
- * gcm_clut_load_from_config:
- **/
-gboolean
-gcm_clut_load_from_config (GcmClut *clut, GError **error)
-{
-	gboolean ret;
-	GKeyFile *file;
-	GError *error_local = NULL;
-	gchar *filename = NULL;
-
-	g_return_val_if_fail (GCM_IS_CLUT (clut), FALSE);
-	g_return_val_if_fail (clut->priv->id != NULL, FALSE);
-
-	/* get default config */
-	filename = gcm_clut_get_default_config_location ();
-
-	/* load existing file */
-	file = g_key_file_new ();
-	ret = g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error_local);
-	if (!ret) {
-		if (error != NULL)
-			*error = g_error_new (1, 0, "failed to load from file: %s", error_local->message);
-		g_error_free (error_local);
-		goto out;
-	}
-
-	/* load data */
-	g_free (clut->priv->profile);
-	clut->priv->profile = g_key_file_get_string (file, clut->priv->id, "profile", NULL);
-	clut->priv->gamma = g_key_file_get_double (file, clut->priv->id, "gamma", &error_local);
-	if (error_local != NULL) {
-		clut->priv->gamma = gconf_client_get_float (clut->priv->gconf_client, "/apps/gnome-color-manager/default_gamma", NULL);
-		if (clut->priv->gamma < 0.1f)
-			clut->priv->gamma = 1.0f;
-		g_clear_error (&error_local);
-	}
-	clut->priv->brightness = g_key_file_get_double (file, clut->priv->id, "brightness", &error_local);
-	if (error_local != NULL) {
-		clut->priv->brightness = 0.0f;
-		g_clear_error (&error_local);
-	}
-	clut->priv->contrast = g_key_file_get_double (file, clut->priv->id, "contrast", &error_local);
-	if (error_local != NULL) {
-		clut->priv->contrast = 100.0f;
-		g_clear_error (&error_local);
-	}
-
-	/* load this */
-	ret = gcm_clut_load_from_profile (clut, &error_local);
-	if (!ret) {
-		if (error != NULL)
-			*error = g_error_new (1, 0, "failed to load from config: %s", error_local->message);
-		g_error_free (error_local);
-		goto out;
-	}
-out:
-	g_free (filename);
-	g_key_file_free (file);
-	return ret;
-}
-
-/**
- * gcm_clut_save_to_config:
- **/
-gboolean
-gcm_clut_save_to_config (GcmClut *clut, GError **error)
-{
-	GKeyFile *keyfile = NULL;
-	gboolean ret;
-	gchar *data;
-	gchar *dirname;
-	GFile *file = NULL;
-	gchar *filename = NULL;
-
-	g_return_val_if_fail (GCM_IS_CLUT (clut), FALSE);
-	g_return_val_if_fail (clut->priv->id != NULL, FALSE);
-
-	/* get default config */
-	filename = gcm_clut_get_default_config_location ();
-
-	/* directory exists? */
-	dirname = g_path_get_dirname (filename);
-	ret = g_file_test (dirname, G_FILE_TEST_IS_DIR);
-	if (!ret) {
-		file = g_file_new_for_path (dirname);
-		ret = g_file_make_directory_with_parents (file, NULL, error);
-		if (!ret)
-			goto out;
-	}
-
-	/* if not ever created, then just create a dummy file */
-	ret = g_file_test (filename, G_FILE_TEST_EXISTS);
-	if (!ret) {
-		ret = g_file_set_contents (filename, "#created today", -1, error);
-		if (!ret)
-			goto out;
-	}
-
-	/* load existing file */
-	keyfile = g_key_file_new ();
-	ret = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error);
-	if (!ret)
-		goto out;
-
-	/* save data */
-	if (clut->priv->profile == NULL)
-		g_key_file_remove_key (keyfile, clut->priv->id, "profile", NULL);
-	else
-		g_key_file_set_string (keyfile, clut->priv->id, "profile", clut->priv->profile);
-	g_key_file_set_double (keyfile, clut->priv->id, "gamma", clut->priv->gamma);
-	g_key_file_set_double (keyfile, clut->priv->id, "brightness", clut->priv->brightness);
-	g_key_file_set_double (keyfile, clut->priv->id, "contrast", clut->priv->contrast);
-
-	/* save contents */
-	data = g_key_file_to_data (keyfile, NULL, error);
-	if (data == NULL)
-		goto out;
-	ret = g_file_set_contents (filename, data, -1, error);
-	if (!ret)
-		goto out;
-out:
-	g_free (filename);
-	g_free (dirname);
-	if (file != NULL)
-		g_object_unref (file);
-	if (keyfile != NULL)
-		g_key_file_free (keyfile);
-	return ret;
-}
-
-/**
  * gcm_clut_get_adjusted_value:
  **/
 static guint
diff --git a/src/gcm-clut.h b/src/gcm-clut.h
index a8c9280..e9bef1d 100644
--- a/src/gcm-clut.h
+++ b/src/gcm-clut.h
@@ -62,9 +62,6 @@ typedef struct {
 
 GType		 gcm_clut_get_type		  	(void);
 GcmClut		*gcm_clut_new				(void);
-gboolean	 gcm_clut_set_from_data			(GcmClut		*clut,
-							 const GcmClutData	*data,
-							 guint			 size);
 gboolean	 gcm_clut_load_from_profile		(GcmClut		*clut,
 							 GError			**error);
 gboolean	 gcm_clut_load_from_config		(GcmClut		*clut,
diff --git a/src/gcm-device.c b/src/gcm-device.c
new file mode 100644
index 0000000..b211212
--- /dev/null
+++ b/src/gcm-device.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 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 device.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gcm-device
+ * @short_description: Color managed device object
+ *
+ * This object represents a device that can be colour managed.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <math.h>
+#include <gio/gio.h>
+#include <gconf/gconf-client.h>
+#include <libgnomeui/gnome-rr.h>
+
+#include "gcm-device.h"
+#include "gcm-profile.h"
+
+#include "egg-debug.h"
+
+static void     gcm_device_finalize	(GObject     *object);
+
+#define GCM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DEVICE, GcmDevicePrivate))
+
+/**
+ * GcmDevicePrivate:
+ *
+ * Private #GcmDevice data
+ **/
+struct _GcmDevicePrivate
+{
+	gfloat				 gamma;
+	gfloat				 brightness;
+	gfloat				 contrast;
+	GcmDeviceType			 type;
+	gchar				*id;
+	gchar				*profile;
+	gchar				*description;
+	gchar				*title;
+	gchar				*copyright;
+	GConfClient			*gconf_client;
+	GnomeRROutput			*native_device_xrandr;
+};
+
+enum {
+	PROP_0,
+	PROP_TYPE,
+	PROP_ID,
+	PROP_GAMMA,
+	PROP_BRIGHTNESS,
+	PROP_CONTRAST,
+	PROP_PROFILE,
+	PROP_COPYRIGHT,
+	PROP_DESCRIPTION,
+	PROP_TITLE,
+	PROP_NATIVE_DEVICE_XRANDR,
+	PROP_LAST
+};
+
+G_DEFINE_TYPE (GcmDevice, gcm_device, G_TYPE_OBJECT)
+
+/**
+ * gcm_device_get_default_config_location:
+ **/
+static gchar *
+gcm_device_get_default_config_location (void)
+{
+	gchar *filename;
+
+	/* create default path */
+	filename = g_build_filename (g_get_user_config_dir (), "gnome-color-manager", "config.dat", NULL);
+
+	return filename;
+}
+
+/**
+ * gcm_device_load_from_profile:
+ **/
+static gboolean
+gcm_device_load_from_profile (GcmDevice *device, GError **error)
+{
+	gboolean ret = TRUE;
+	GcmProfile *profile = NULL;
+	GError *error_local = NULL;
+
+	g_return_val_if_fail (GCM_IS_DEVICE (device), FALSE);
+
+	/* no profile to load */
+	if (device->priv->profile == NULL) {
+		g_free (device->priv->copyright);
+		g_free (device->priv->description);
+		device->priv->copyright = NULL;
+		device->priv->description = NULL;
+		goto out;
+	}
+
+	/* create new profile instance */
+	profile = gcm_profile_new ();
+
+	/* load the profile */
+	ret = gcm_profile_parse (profile, device->priv->profile, &error_local);
+	if (!ret) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to set from profile: %s", error_local->message);
+		g_error_free (error_local);
+		goto out;
+	}
+
+	/* copy the description */
+	g_free (device->priv->copyright);
+	g_free (device->priv->description);
+	g_object_get (profile,
+		      "copyright", &device->priv->copyright,
+		      "description", &device->priv->description,
+		      NULL);
+out:
+	if (profile != NULL)
+		g_object_unref (profile);
+	return ret;
+}
+
+/**
+ * gcm_device_get_id:
+ **/
+const gchar *
+gcm_device_get_id (GcmDevice *device)
+{
+	g_return_val_if_fail (GCM_IS_DEVICE (device), NULL);
+	return device->priv->id;
+}
+
+/**
+ * gcm_device_load:
+ **/
+gboolean
+gcm_device_load (GcmDevice *device, GError **error)
+{
+	gboolean ret;
+	GKeyFile *file;
+	GError *error_local = NULL;
+	gchar *filename = NULL;
+
+	g_return_val_if_fail (GCM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (device->priv->id != NULL, FALSE);
+
+	/* get default config */
+	filename = gcm_device_get_default_config_location ();
+
+	/* load existing file */
+	file = g_key_file_new ();
+	ret = g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error_local);
+	if (!ret) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to load from file: %s", error_local->message);
+		g_error_free (error_local);
+		goto out;
+	}
+
+	/* load data */
+	g_free (device->priv->profile);
+	device->priv->profile = g_key_file_get_string (file, device->priv->id, "profile", NULL);
+	device->priv->gamma = g_key_file_get_double (file, device->priv->id, "gamma", &error_local);
+	if (error_local != NULL) {
+		device->priv->gamma = gconf_client_get_float (device->priv->gconf_client, "/apps/gnome-color-manager/default_gamma", NULL);
+		if (device->priv->gamma < 0.1f)
+			device->priv->gamma = 1.0f;
+		g_clear_error (&error_local);
+	}
+	device->priv->brightness = g_key_file_get_double (file, device->priv->id, "brightness", &error_local);
+	if (error_local != NULL) {
+		device->priv->brightness = 0.0f;
+		g_clear_error (&error_local);
+	}
+	device->priv->contrast = g_key_file_get_double (file, device->priv->id, "contrast", &error_local);
+	if (error_local != NULL) {
+		device->priv->contrast = 100.0f;
+		g_clear_error (&error_local);
+	}
+
+	/* load this */
+	ret = gcm_device_load_from_profile (device, &error_local);
+	if (!ret) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to load from config: %s", error_local->message);
+		g_error_free (error_local);
+		goto out;
+	}
+out:
+	g_free (filename);
+	g_key_file_free (file);
+	return ret;
+}
+
+/**
+ * gcm_device_save:
+ **/
+gboolean
+gcm_device_save (GcmDevice *device, GError **error)
+{
+	GKeyFile *keyfile = NULL;
+	gboolean ret;
+	gchar *data;
+	gchar *dirname;
+	GFile *file = NULL;
+	gchar *filename = NULL;
+
+	g_return_val_if_fail (GCM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (device->priv->id != NULL, FALSE);
+
+	/* get default config */
+	filename = gcm_device_get_default_config_location ();
+
+	/* directory exists? */
+	dirname = g_path_get_dirname (filename);
+	ret = g_file_test (dirname, G_FILE_TEST_IS_DIR);
+	if (!ret) {
+		file = g_file_new_for_path (dirname);
+		ret = g_file_make_directory_with_parents (file, NULL, error);
+		if (!ret)
+			goto out;
+	}
+
+	/* if not ever created, then just create a dummy file */
+	ret = g_file_test (filename, G_FILE_TEST_EXISTS);
+	if (!ret) {
+		ret = g_file_set_contents (filename, "#created today", -1, error);
+		if (!ret)
+			goto out;
+	}
+
+	/* load existing file */
+	keyfile = g_key_file_new ();
+	ret = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error);
+	if (!ret)
+		goto out;
+
+	/* save data */
+	if (device->priv->profile == NULL)
+		g_key_file_remove_key (keyfile, device->priv->id, "profile", NULL);
+	else
+		g_key_file_set_string (keyfile, device->priv->id, "profile", device->priv->profile);
+	g_key_file_set_double (keyfile, device->priv->id, "gamma", device->priv->gamma);
+	g_key_file_set_double (keyfile, device->priv->id, "brightness", device->priv->brightness);
+	g_key_file_set_double (keyfile, device->priv->id, "contrast", device->priv->contrast);
+
+	/* save contents */
+	data = g_key_file_to_data (keyfile, NULL, error);
+	if (data == NULL)
+		goto out;
+	ret = g_file_set_contents (filename, data, -1, error);
+	if (!ret)
+		goto out;
+out:
+	g_free (filename);
+	g_free (dirname);
+	if (file != NULL)
+		g_object_unref (file);
+	if (keyfile != NULL)
+		g_key_file_free (keyfile);
+	return ret;
+}
+
+/**
+ * gcm_device_get_property:
+ **/
+static void
+gcm_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	GcmDevice *device = GCM_DEVICE (object);
+	GcmDevicePrivate *priv = device->priv;
+
+	switch (prop_id) {
+	case PROP_TYPE:
+		g_value_set_uint (value, priv->type);
+		break;
+	case PROP_ID:
+		g_value_set_string (value, priv->id);
+		break;
+	case PROP_GAMMA:
+		g_value_set_float (value, priv->gamma);
+		break;
+	case PROP_BRIGHTNESS:
+		g_value_set_float (value, priv->brightness);
+		break;
+	case PROP_CONTRAST:
+		g_value_set_float (value, priv->contrast);
+		break;
+	case PROP_PROFILE:
+		g_value_set_string (value, priv->profile);
+		break;
+	case PROP_COPYRIGHT:
+		g_value_set_string (value, priv->copyright);
+		break;
+	case PROP_DESCRIPTION:
+		g_value_set_string (value, priv->description);
+		break;
+	case PROP_TITLE:
+		g_value_set_string (value, priv->title);
+		break;
+	case PROP_NATIVE_DEVICE_XRANDR:
+		g_value_set_pointer (value, priv->native_device_xrandr);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_device_set_property:
+ **/
+static void
+gcm_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	GcmDevice *device = GCM_DEVICE (object);
+	GcmDevicePrivate *priv = device->priv;
+
+	switch (prop_id) {
+	case PROP_TYPE:
+		priv->type = g_value_get_uint (value);
+		break;
+	case PROP_ID:
+		g_free (priv->id);
+		priv->id = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_TITLE:
+		g_free (priv->title);
+		priv->title = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_PROFILE:
+		g_free (priv->profile);
+		priv->profile = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_GAMMA:
+		priv->gamma = g_value_get_float (value);
+		break;
+	case PROP_BRIGHTNESS:
+		priv->brightness = g_value_get_float (value);
+		break;
+	case PROP_CONTRAST:
+		priv->contrast = g_value_get_float (value);
+		break;
+	case PROP_NATIVE_DEVICE_XRANDR:
+		priv->native_device_xrandr = g_value_get_pointer (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_device_class_init:
+ **/
+static void
+gcm_device_class_init (GcmDeviceClass *klass)
+{
+	GParamSpec *pspec;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gcm_device_finalize;
+	object_class->get_property = gcm_device_get_property;
+	object_class->set_property = gcm_device_set_property;
+
+	/**
+	 * GcmDevice:type:
+	 */
+	pspec = g_param_spec_uint ("type", NULL, NULL,
+				   0, G_MAXUINT, 0,
+				   G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_TYPE, pspec);
+
+	/**
+	 * GcmDevice:id:
+	 */
+	pspec = g_param_spec_string ("id", NULL, NULL,
+				     NULL,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_ID, pspec);
+
+	/**
+	 * GcmDevice:gamma:
+	 */
+	pspec = g_param_spec_float ("gamma", NULL, NULL,
+				    0.0, G_MAXFLOAT, 1.01,
+				    G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_GAMMA, pspec);
+
+	/**
+	 * GcmDevice:brightness:
+	 */
+	pspec = g_param_spec_float ("brightness", NULL, NULL,
+				    0.0, G_MAXFLOAT, 1.02,
+				    G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_BRIGHTNESS, pspec);
+
+	/**
+	 * GcmDevice:contrast:
+	 */
+	pspec = g_param_spec_float ("contrast", NULL, NULL,
+				    0.0, G_MAXFLOAT, 1.03,
+				    G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_CONTRAST, pspec);
+
+	/**
+	 * GcmDevice:copyright:
+	 */
+	pspec = g_param_spec_string ("copyright", NULL, NULL,
+				     NULL,
+				     G_PARAM_READABLE);
+	g_object_class_install_property (object_class, PROP_COPYRIGHT, pspec);
+
+	/**
+	 * GcmDevice:description:
+	 */
+	pspec = g_param_spec_string ("description", NULL, NULL,
+				     NULL,
+				     G_PARAM_READABLE);
+	g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
+
+	/**
+	 * GcmDevice:title:
+	 */
+	pspec = g_param_spec_string ("title", NULL, NULL,
+				     NULL,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_TITLE, pspec);
+
+	/**
+	 * GcmDevice:profile:
+	 */
+	pspec = g_param_spec_string ("profile", NULL, NULL,
+				     NULL,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_PROFILE, pspec);
+
+	/**
+	 * GcmDevice:native-device-xrandr:
+	 */
+	pspec = g_param_spec_pointer ("native-device-xrandr", NULL, NULL,
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_NATIVE_DEVICE_XRANDR, pspec);
+
+	g_type_class_add_private (klass, sizeof (GcmDevicePrivate));
+}
+
+/**
+ * gcm_device_init:
+ **/
+static void
+gcm_device_init (GcmDevice *device)
+{
+	device->priv = GCM_DEVICE_GET_PRIVATE (device);
+	device->priv->native_device_xrandr = NULL;
+	device->priv->profile = NULL;
+	device->priv->gconf_client = gconf_client_get_default ();
+	device->priv->gamma = gconf_client_get_float (device->priv->gconf_client, "/apps/gnome-color-manager/default_gamma", NULL);
+	if (device->priv->gamma < 0.1f) {
+		egg_warning ("failed to get setup parameters");
+		device->priv->gamma = 1.0f;
+	}
+	device->priv->brightness = 0.0f;
+	device->priv->contrast = 100.f;
+}
+
+/**
+ * gcm_device_finalize:
+ **/
+static void
+gcm_device_finalize (GObject *object)
+{
+	GcmDevice *device = GCM_DEVICE (object);
+	GcmDevicePrivate *priv = device->priv;
+
+	g_free (priv->description);
+	g_free (priv->copyright);
+	g_free (priv->title);
+	g_free (priv->id);
+	g_object_unref (priv->gconf_client);
+
+	G_OBJECT_CLASS (gcm_device_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_device_new:
+ *
+ * Return value: a new GcmDevice object.
+ **/
+GcmDevice *
+gcm_device_new (void)
+{
+	GcmDevice *device;
+	device = g_object_new (GCM_TYPE_DEVICE, NULL);
+	return GCM_DEVICE (device);
+}
+
diff --git a/src/gcm-device.h b/src/gcm-device.h
new file mode 100644
index 0000000..438ea48
--- /dev/null
+++ b/src/gcm-device.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 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 device.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GCM_DEVICE_H
+#define __GCM_DEVICE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_DEVICE			(gcm_device_get_type ())
+#define GCM_DEVICE(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DEVICE, GcmDevice))
+#define GCM_DEVICE_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DEVICE, GcmDeviceClass))
+#define GCM_IS_DEVICE(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DEVICE))
+#define GCM_IS_DEVICE_CLASS(k)		(G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DEVICE))
+#define GCM_DEVICE_GET_CLASS(o)		(G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DEVICE, GcmDeviceClass))
+
+typedef struct _GcmDevicePrivate	GcmDevicePrivate;
+typedef struct _GcmDevice		GcmDevice;
+typedef struct _GcmDeviceClass		GcmDeviceClass;
+
+struct _GcmDevice
+{
+	 GObject			 parent;
+	 GcmDevicePrivate		*priv;
+};
+
+struct _GcmDeviceClass
+{
+	GObjectClass	parent_class;
+	/* padding for future expansion */
+	void (*_gcm_reserved1) (void);
+	void (*_gcm_reserved2) (void);
+	void (*_gcm_reserved3) (void);
+	void (*_gcm_reserved4) (void);
+	void (*_gcm_reserved5) (void);
+};
+
+typedef enum {
+	GCM_DEVICE_TYPE_DISPLAY,
+	GCM_DEVICE_TYPE_SCANNER,
+	GCM_DEVICE_TYPE_PRINTER,
+	GCM_DEVICE_TYPE_CAMERA,
+	GCM_DEVICE_TYPE_LAST
+} GcmDeviceType;
+
+GType		 gcm_device_get_type		  	(void);
+GcmDevice	*gcm_device_new				(void);
+const gchar	*gcm_device_get_id			(GcmDevice		*device);
+gboolean	 gcm_device_load			(GcmDevice		*device,
+							 GError			**error);
+gboolean	 gcm_device_save			(GcmDevice		*device,
+							 GError			**error);
+
+G_END_DECLS
+
+#endif /* __GCM_DEVICE_H */
+
diff --git a/src/gcm-inspect.c b/src/gcm-inspect.c
index 65dff45..92e0f05 100644
--- a/src/gcm-inspect.c
+++ b/src/gcm-inspect.c
@@ -21,6 +21,7 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include <libgnomeui/gnome-rr.h>
 
 #include "egg-debug.h"
 
diff --git a/src/gcm-prefs.c b/src/gcm-prefs.c
index be4d64f..26a03ea 100644
--- a/src/gcm-prefs.c
+++ b/src/gcm-prefs.c
@@ -25,6 +25,7 @@
 #include <unique/unique.h>
 #include <glib/gstdio.h>
 #include <gudev/gudev.h>
+#include <libgnomeui/gnome-rr.h>
 
 #include "egg-debug.h"
 
@@ -32,20 +33,20 @@
 #include "gcm-profile.h"
 #include "gcm-calibrate.h"
 #include "gcm-brightness.h"
+#include "gcm-client.h"
 
 static GtkBuilder *builder = NULL;
 static GtkListStore *list_store_devices = NULL;
-static guint current_device = 0;
-static GcmClut *current_clut = NULL;
+static GcmDevice *current_device = NULL;
 static GnomeRRScreen *rr_screen = NULL;
 static GPtrArray *profiles_array = NULL;
 static GUdevClient *client = NULL;
+static GcmClient *gcm_client = NULL;
 
 enum {
 	GPM_DEVICES_COLUMN_ID,
 	GPM_DEVICES_COLUMN_ICON,
-	GPM_DEVICES_COLUMN_TEXT,
-	GPM_DEVICES_COLUMN_CLUT,
+	GPM_DEVICES_COLUMN_TITLE,
 	GPM_DEVICES_COLUMN_LAST
 };
 
@@ -98,7 +99,9 @@ gcm_prefs_calibrate_cb (GtkWidget *widget, gpointer data)
 	guint percentage = G_MAXUINT;
 
 	/* get the device */
-	output = gnome_rr_screen_get_output_by_id (rr_screen, current_device);
+	g_object_get (current_device,
+		      "native-device-xrandr", &output,
+		      NULL);
 	if (output == NULL) {
 		egg_warning ("failed to get output");
 		goto out;
@@ -237,7 +240,7 @@ out:
 		g_object_unref (brightness);
 
 	/* need to set the gamma back to the default after calibration */
-	ret = gcm_utils_set_output_gamma (output, &error);
+	ret = gcm_utils_set_gamma_for_device (current_device, &error);
 	if (!ret) {
 		egg_warning ("failed to set output gamma: %s", error->message);
 		g_error_free (error);
@@ -312,8 +315,8 @@ gcm_prefs_add_devices_columns (GtkTreeView *treeview)
 	/* column for text */
 	renderer = gtk_cell_renderer_text_new ();
 	column = gtk_tree_view_column_new_with_attributes (_("Label"), renderer,
-							   "markup", GPM_DEVICES_COLUMN_TEXT, NULL);
-	gtk_tree_view_column_set_sort_column_id (column, GPM_DEVICES_COLUMN_TEXT);
+							   "markup", GPM_DEVICES_COLUMN_TITLE, NULL);
+	gtk_tree_view_column_set_sort_column_id (column, GPM_DEVICES_COLUMN_TITLE);
 	gtk_tree_view_append_column (treeview, column);
 	gtk_tree_view_column_set_expand (column, TRUE);
 }
@@ -333,6 +336,8 @@ gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean dat
 	gfloat contrast;
 	const gchar *filename;
 	guint i;
+	gchar *id;
+	GcmDeviceType type;
 
 	/* This will only work in single or browse selection mode! */
 	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
@@ -340,16 +345,20 @@ gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean dat
 		return;
 	}
 
+	/* get id */
 	gtk_tree_model_get (model, &iter,
-			    GPM_DEVICES_COLUMN_ID, &current_device,
-			    GPM_DEVICES_COLUMN_CLUT, &current_clut,
+			    GPM_DEVICES_COLUMN_ID, &id,
 			    -1);
 
 	/* show transaction_id */
-	egg_debug ("selected row is: %i", current_device);
+	egg_debug ("selected device is: %s", id);
+	current_device = gcm_client_get_device_by_id (gcm_client, id);
+	g_object_get (current_device,
+		      "type", &type,
+		      NULL);
 
 	/* not a xrandr device */
-	if (current_device == G_MAXUINT) {
+	if (type == GCM_DEVICE_TYPE_SCANNER) {
 		widget = GTK_WIDGET (gtk_builder_get_object (builder, "expander1"));
 		gtk_widget_hide (widget);
 		widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_reset"));
@@ -365,7 +374,7 @@ gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean dat
 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_reset"));
 	gtk_widget_show (widget);
 
-	g_object_get (current_clut,
+	g_object_get (current_device,
 		      "profile", &profile,
 		      "gamma", &localgamma,
 		      "brightness", &brightness,
@@ -396,92 +405,54 @@ gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean dat
 		}
 	}
 out:
+	g_free (id);
 	g_free (profile);
 }
 
 /**
- * gcm_prefs_has_device:
- **/
-static gboolean
-gcm_prefs_has_device (guint id)
-{
-	GtkTreeIter iter;
-	GtkTreeModel *model;
-	guint id_tmp;
-	gboolean ret;
-
-	/* get first element */
-	model = GTK_TREE_MODEL (list_store_devices);
-	ret = gtk_tree_model_get_iter_first (model, &iter);
-	if (!ret)
-		goto out;
-
-	/* not yet found */
-	ret = FALSE;
-
-	/* get the other elements */
-	do {
-		gtk_tree_model_get (model, &iter,
-				    GPM_DEVICES_COLUMN_ID, &id_tmp,
-				    -1);
-		if (id_tmp == id) {
-			ret = TRUE;
-			break;
-		}
-	} while (gtk_tree_model_iter_next (model, &iter));
-out:
-	return ret;
-}
-
-/**
- * gcm_prefs_add_xrandr_device:
+ * gcm_prefs_add_device_xrandr:
  **/
 static void
-gcm_prefs_add_xrandr_device (GnomeRROutput *output)
+gcm_prefs_add_device_xrandr (GcmDevice *device)
 {
 	GtkTreeIter iter;
-	gchar *name;
-	guint id;
-	GcmClut *clut;
-	GError *error = NULL;
+	gchar *title;
+	gchar *id;
 	gboolean ret;
+	GError *error = NULL;
 
 	/* get details */
-	id = gnome_rr_output_get_id (output);
-	name = gcm_utils_get_output_name (output);
+	g_object_get (device,
+		      "id", &id,
+		      "title", &title,
+		      NULL);
 
-	/* if nothing connected then ignore */
-	ret = gnome_rr_output_is_connected (output);
+	/* load this */
+	ret = gcm_device_load (device, &error);
 	if (!ret) {
-		egg_debug ("%s is not connected", name);
-		goto out;
-	}
-
-	/* are we already in the list */
-	ret = gcm_prefs_has_device (id);
-	if (ret) {
-		egg_debug ("%s already added", name);
+		egg_warning ("failed to load device: %s", error->message);
+		g_error_free (error);
 		goto out;
 	}
 
-	/* create a clut for this output */
-	clut = gcm_utils_get_clut_for_output (output, &error);
-	if (clut == NULL) {
-		egg_warning ("failed to add device: %s", error->message);
+	/* set the gamma on the device */
+	ret = gcm_utils_set_gamma_for_device (device, &error);
+	if (!ret) {
+		egg_warning ("failed to set output gamma: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 
 	/* add to list */
-	egg_debug ("add %s to device list", name);
+	egg_debug ("add %s to device list", id);
 	gtk_list_store_append (list_store_devices, &iter);
 	gtk_list_store_set (list_store_devices, &iter,
 			    GPM_DEVICES_COLUMN_ID, id,
-			    GPM_DEVICES_COLUMN_TEXT, name,
-			    GPM_DEVICES_COLUMN_CLUT, clut,
+			    GPM_DEVICES_COLUMN_TITLE, title,
 			    GPM_DEVICES_COLUMN_ICON, "video-display", -1);
 out:
-	g_free (name);
+	g_free (id);
+	g_free (title);
 }
 
 /**
@@ -550,10 +521,13 @@ gcm_prefs_history_type_combo_changed_cb (GtkWidget *widget, gpointer data)
 	guint active;
 	gchar *copyright = NULL;
 	gchar *description = NULL;
+	gchar *profile_old = NULL;
 	const gchar *filename = NULL;
 	gboolean ret;
 	GError *error = NULL;
-	GnomeRROutput *output;
+	GcmProfile *profile = NULL;
+	gboolean changed;
+	GcmDeviceType type;
 
 	active = gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
 	egg_debug ("now %i", active);
@@ -563,7 +537,7 @@ gcm_prefs_history_type_combo_changed_cb (GtkWidget *widget, gpointer data)
 	egg_debug ("profile=%s", filename);
 
 	/* not a xrandr device */
-	if (current_clut == NULL) {
+	if (current_device == NULL) {
 		widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_title_copyright"));
 		gtk_widget_hide (widget);
 		widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_copyright"));
@@ -575,22 +549,35 @@ gcm_prefs_history_type_combo_changed_cb (GtkWidget *widget, gpointer data)
 		goto out;
 	}
 
-	/* set new profile */
-	g_object_set (current_clut,
-		      "profile", filename,
+	/* see if it's changed */
+	g_object_get (current_device,
+		      "profile", &profile_old,
+		      "type", &type,
 		      NULL);
+	changed = ((g_strcmp0 (profile_old, filename) != 0));
+
+	/* set new profile */
+	if (changed) {
+		g_object_set (current_device,
+			      "profile", filename,
+			      NULL);
+	}
 
 	/* get new data */
-	ret = gcm_clut_load_from_profile (current_clut, &error);
-	if (!ret) {
-		egg_warning ("failed to load profile: %s", error->message);
-		g_error_free (error);
-		goto out;
+	if (filename != NULL) {
+		profile = gcm_profile_new ();
+		ret = gcm_profile_parse (profile, filename, &error);
+		if (!ret) {
+			egg_warning ("failed to load profile: %s", error->message);
+			g_error_free (error);
+			goto out;
+		}
+		/* get the new details from the profile */
+		g_object_get (profile,
+			      "copyright", &copyright,
+			      "description", &description,
+			      NULL);
 	}
-	g_object_get (current_clut,
-		      "copyright", &copyright,
-		      "description", &description,
-		      NULL);
 
 	/* set new descriptions */
 	if (copyright == NULL) {
@@ -629,29 +616,29 @@ gcm_prefs_history_type_combo_changed_cb (GtkWidget *widget, gpointer data)
 		gtk_widget_show (widget);
 	}
 
-	/* save new profile */
-	ret = gcm_clut_save_to_config (current_clut, &error);
-	if (!ret) {
-		egg_warning ("failed to save config: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-
-	/* get the device */
-	output = gnome_rr_screen_get_output_by_id (rr_screen, current_device);
-	if (output == NULL) {
-		egg_warning ("failed to get output");
-		goto out;
-	}
+	/* only save and set if changed */
+	if (changed) {
+		/* save new profile */
+		ret = gcm_device_save (current_device, &error);
+		if (!ret) {
+			egg_warning ("failed to save config: %s", error->message);
+			g_error_free (error);
+			goto out;
+		}
 
-	/* actually set the new profile */
-	ret = gcm_utils_set_output_gamma (output, &error);
-	if (!ret) {
-		egg_warning ("failed to set output gamma: %s", error->message);
-		g_error_free (error);
-		goto out;
+		/* set the gamma for display types */
+		if (type == GCM_DEVICE_TYPE_DISPLAY) {
+			ret = gcm_utils_set_gamma_for_device (current_device, &error);
+			if (!ret) {
+				egg_warning ("failed to set output gamma: %s", error->message);
+				g_error_free (error);
+				goto out;
+			}
+		}
 	}
 out:
+	if (profile != NULL)
+		g_object_unref (profile);
 	g_free (copyright);
 	g_free (description);
 }
@@ -668,7 +655,6 @@ gcm_prefs_slider_changed_cb (GtkRange *range, gpointer *user_data)
 	GtkWidget *widget;
 	gboolean ret;
 	GError *error = NULL;
-	GnomeRROutput *output;
 
 	/* get values */
 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "hscale_gamma"));
@@ -678,29 +664,22 @@ gcm_prefs_slider_changed_cb (GtkRange *range, gpointer *user_data)
 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "hscale_contrast"));
 	contrast = gtk_range_get_value (GTK_RANGE (widget));
 
-	g_object_set (current_clut,
+	g_object_set (current_device,
 		      "gamma", localgamma,
 		      "brightness", brightness,
 		      "contrast", contrast,
 		      NULL);
 
 	/* save new profile */
-	ret = gcm_clut_save_to_config (current_clut, &error);
+	ret = gcm_device_save (current_device, &error);
 	if (!ret) {
 		egg_warning ("failed to save config: %s", error->message);
 		g_error_free (error);
 		goto out;
 	}
 
-	/* get the device */
-	output = gnome_rr_screen_get_output_by_id (rr_screen, current_device);
-	if (output == NULL) {
-		egg_warning ("failed to get output");
-		goto out;
-	}
-
 	/* actually set the new profile */
-	ret = gcm_utils_set_output_gamma (output, &error);
+	ret = gcm_utils_set_gamma_for_device (current_device, &error);
 	if (!ret) {
 		egg_warning ("failed to set output gamma: %s", error->message);
 		g_error_free (error);
@@ -711,23 +690,6 @@ out:
 }
 
 /**
- * gcm_prefs_randr_event_cb:
- **/
-static void
-gcm_prefs_randr_event_cb (GnomeRRScreen *screen, gpointer data)
-{
-	GnomeRROutput **outputs;
-	guint i;
-
-	egg_debug ("screens may have changed");
-
-	/* replug devices */
-	outputs = gnome_rr_screen_list_outputs (rr_screen);
-	for (i=0; outputs[i] != NULL; i++)
-		gcm_prefs_add_xrandr_device (outputs[i]);
-}
-
-/**
  * gcm_prefs_has_hardware_device_attached:
  **/
 static gboolean
@@ -788,72 +750,86 @@ gcm_prefs_uevent_cb (GUdevClient *client_, const gchar *action, GUdevDevice *dev
 }
 
 /**
- * gcm_prefs_coldplug_devices_xrandr:
+ * gcm_prefs_add_device_scanner:
  **/
 static void
-gcm_prefs_coldplug_devices_xrandr (void)
+gcm_prefs_add_device_scanner (GcmDevice *device)
 {
-	GnomeRROutput **outputs;
-	guint i;
+	GtkTreeIter iter;
+	gchar *title;
+	gchar *id;
 
-	outputs = gnome_rr_screen_list_outputs (rr_screen);
-	for (i=0; outputs[i] != NULL; i++)
-		gcm_prefs_add_xrandr_device (outputs[i]);
+	/* get details */
+	g_object_get (device,
+		      "id", &id,
+		      "title", &title,
+		      NULL);
+
+	/* add to list */
+	gtk_list_store_append (list_store_devices, &iter);
+	gtk_list_store_set (list_store_devices, &iter,
+			    GPM_DEVICES_COLUMN_ID, id,
+			    GPM_DEVICES_COLUMN_TITLE, title,
+			    GPM_DEVICES_COLUMN_ICON, "camera-photo", -1);
+	g_free (id);
+	g_free (title);
 }
 
 /**
- * gcm_prefs_add_scanner_device:
+ * gcm_prefs_added_cb:
  **/
 static void
-gcm_prefs_add_scanner_device (GUdevDevice *device)
+gcm_prefs_added_cb (GcmClient *gcm_client_, GcmDevice *gcm_device, gpointer user_data)
 {
-	GtkTreeIter iter;
-	gchar *name;
+	GcmDeviceType type;
+	egg_debug ("added: %s", gcm_device_get_id (gcm_device));
 
-	/* get details */
-	name = g_strdup_printf ("%s - %s",
-				g_udev_device_get_property (device, "ID_VENDOR"),
-				g_udev_device_get_property (device, "ID_MODEL"));
-
-	//TODO: use g_udev_device_get_sysfs_path (device) as id
+	/* get the type of the device */
+	g_object_get (gcm_device,
+		      "type", &type,
+		      NULL);
 
-	/* add to list */
-	egg_debug ("add %s to device list", name);
-	gtk_list_store_append (list_store_devices, &iter);
-	gtk_list_store_set (list_store_devices, &iter,
-			    GPM_DEVICES_COLUMN_ID, G_MAXUINT,
-			    GPM_DEVICES_COLUMN_TEXT, name,
-			    GPM_DEVICES_COLUMN_CLUT, NULL,
-			    GPM_DEVICES_COLUMN_ICON, "camera-photo", -1);
-	g_free (name);
+	/* add the device */
+	if (type == GCM_DEVICE_TYPE_DISPLAY)
+		gcm_prefs_add_device_xrandr (gcm_device);
+	else if (type == GCM_DEVICE_TYPE_SCANNER)
+		gcm_prefs_add_device_scanner (gcm_device);
 }
 
 /**
- * gcm_prefs_coldplug_devices_scanner:
+ * gcm_prefs_removed_cb:
  **/
 static void
-gcm_prefs_coldplug_devices_scanner (void)
+gcm_prefs_removed_cb (GcmClient *gcm_client_, GcmDevice *gcm_device, gpointer user_data)
 {
-	GList *devices;
-	GList *l;
-	GUdevDevice *device;
-	const gchar *value;
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	const gchar *id;
+	gchar *id_tmp;
+	gboolean ret;
 
-	/* get all USB devices */
-	devices = g_udev_client_query_by_subsystem (client, "usb");
-	for (l = devices; l != NULL; l = l->next) {
-		device = l->data;
+	/* remove */
+	id = gcm_device_get_id (gcm_device);
+	egg_debug ("removed: %s", id);
 
-		/* sane is slightly odd in a lowercase property, and "yes" as a value rather than "1" */
-		value = g_udev_device_get_property (device, "libsane_matched");
-		if (value != NULL) {
-			egg_debug ("found scanner device: %s", g_udev_device_get_sysfs_path (device));
-			gcm_prefs_add_scanner_device (device);
-		}
-	}
+	/* get first element */
+	model = GTK_TREE_MODEL (list_store_devices);
+	ret = gtk_tree_model_get_iter_first (model, &iter);
+	if (!ret)
+		return;
 
-	g_list_foreach (devices, (GFunc) g_object_unref, NULL);
-	g_list_free (devices);
+	/* get the other elements */
+	do {
+		gtk_tree_model_get (model, &iter,
+				    GPM_DEVICES_COLUMN_ID, &id_tmp,
+				    -1);
+		if (g_strcmp0 (id_tmp, id) == 0) {
+			gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
+			g_free (id_tmp);
+			break;
+		}
+		g_free (id_tmp);
+	} while (gtk_tree_model_iter_next (model, &iter));
 }
 
 /**
@@ -873,6 +849,7 @@ main (int argc, char **argv)
 	GMainLoop *loop;
 	GtkTreeSelection *selection;
 	const gchar *subsystems[] = {"usb", NULL};
+	gboolean ret;
 
 	const GOptionEntry options[] = {
 		{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
@@ -914,16 +891,16 @@ main (int argc, char **argv)
 		goto out;
 	}
 
-	/* use GUdev to find properties */
+	/* use GUdev to find the calibration device */
 	client = g_udev_client_new (subsystems);
 	g_signal_connect (client, "uevent",
 			  G_CALLBACK (gcm_prefs_uevent_cb), NULL);
 
-	/* coldplug calibrate button sensitivity */
+	/* set calibrate button sensitivity */
 	gcm_prefs_check_calibration_hardware ();
 
 	/* create list stores */
-	list_store_devices = gtk_list_store_new (GPM_DEVICES_COLUMN_LAST, G_TYPE_UINT,
+	list_store_devices = gtk_list_store_new (GPM_DEVICES_COLUMN_LAST, G_TYPE_STRING,
 						 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
 
 	/* create transaction_id tree view */
@@ -982,15 +959,24 @@ main (int argc, char **argv)
 	gtk_range_set_range (GTK_RANGE (widget), 1.0f, 100.0f);
 
 	/* get screen */
-	rr_screen = gnome_rr_screen_new (gdk_screen_get_default (), gcm_prefs_randr_event_cb, NULL, &error);
+	rr_screen = gnome_rr_screen_new (gdk_screen_get_default (), NULL, NULL, &error);
 	if (rr_screen == NULL) {
 		egg_warning ("failed to get rr screen: %s", error->message);
 		goto out;
 	}
 
+	/* use a device client array */
+	gcm_client = gcm_client_new ();
+	g_signal_connect (gcm_client, "added", G_CALLBACK (gcm_prefs_added_cb), NULL);
+	g_signal_connect (gcm_client, "removed", G_CALLBACK (gcm_prefs_removed_cb), NULL);
+
 	/* coldplug devices */
-	gcm_prefs_coldplug_devices_xrandr ();
-	gcm_prefs_coldplug_devices_scanner ();
+	ret = gcm_client_coldplug (gcm_client, &error);
+	if (!ret) {
+		egg_warning ("failed to coldplug: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
 
 	/* set the parent window if it is specified */
 	if (xid != 0) {
@@ -1026,6 +1012,8 @@ out:
 		g_object_unref (builder);
 	if (profiles_array != NULL)
 		g_ptr_array_unref (profiles_array);
+	if (gcm_client != NULL)
+		g_object_unref (gcm_client);
 	return retval;
 }
 
diff --git a/src/gcm-utils.c b/src/gcm-utils.c
index eb76ac9..6dcf59b 100644
--- a/src/gcm-utils.c
+++ b/src/gcm-utils.c
@@ -24,9 +24,11 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
+#include <libgnomeui/gnome-rr.h>
+#include <X11/extensions/Xrandr.h>
 
 #include "gcm-utils.h"
-#include "gcm-edid.h"
+#include "gcm-clut.h"
 #include "gcm-xserver.h"
 
 #include "egg-debug.h"
@@ -66,64 +68,11 @@ gcm_utils_output_is_lcd (const gchar *output_name)
 }
 
 /**
- * gcm_utils_get_output_name:
- *
- * Return value: the output name, free with g_free().
- **/
-gchar *
-gcm_utils_get_output_name (GnomeRROutput *output)
-{
-	const guint8 *data;
-	const gchar *output_name;
-	gchar *name = NULL;
-	GcmEdid *edid = NULL;
-	gboolean ret;
-
-	/* if nothing connected then fall back to the connector name */
-	ret = gnome_rr_output_is_connected (output);
-	if (!ret)
-		goto out;
-
-	/* parse the EDID to get a crtc-specific name, not an output specific name */
-	data = gnome_rr_output_get_edid_data (output);
-	if (data == NULL)
-		goto out;
-
-	edid = gcm_edid_new ();
-	ret = gcm_edid_parse (edid, data, NULL);
-	if (!ret) {
-		egg_warning ("failed to parse edid");
-		goto out;
-	}
-
-	/* find the best option */
-	g_object_get (edid, "monitor-name", &name, NULL);
-	if (name == NULL)
-		g_object_get (edid, "ascii-string", &name, NULL);
-	if (name == NULL)
-		g_object_get (edid, "serial-number", &name, NULL);
-
-out:
-	/* fallback to the output name */
-	if (name == NULL) {
-		output_name = gnome_rr_output_get_name (output);
-		ret = gcm_utils_output_is_lcd_internal (output_name);
-		if (ret)
-			output_name = _("Internal LCD");
-		name = g_strdup (output_name);
-	}
-
-	if (edid != NULL)
-		g_object_unref (edid);
-	return name;
-}
-
-/**
  * gcm_utils_get_gamma_size:
  *
  * Return value: the gamma size, or 0 if error;
  **/
-guint
+static guint
 gcm_utils_get_gamma_size (GnomeRRCrtc *crtc, GError **error)
 {
 	guint id;
@@ -149,12 +98,12 @@ out:
 }
 
 /**
- * gcm_utils_set_crtc_gamma:
+ * gcm_utils_set_gamma_for_crtc:
  *
  * Return value: %TRUE for success;
  **/
-gboolean
-gcm_utils_set_crtc_gamma (GnomeRRCrtc *crtc, GcmClut *clut, GError **error)
+static gboolean
+gcm_utils_set_gamma_for_crtc (GnomeRRCrtc *crtc, GcmClut *clut, GError **error)
 {
 	guint id;
 	gboolean ret = TRUE;
@@ -211,110 +160,100 @@ out:
 }
 
 /**
- * gcm_utils_get_clut_for_output:
+ * gcm_utils_set_gamma_for_device:
+ *
+ * Return value: %TRUE for success;
  **/
-GcmClut *
-gcm_utils_get_clut_for_output (GnomeRROutput *output, GError **error)
+gboolean
+gcm_utils_set_gamma_for_device (GcmDevice *device, GError **error)
 {
-	gchar *name = NULL;
-	gboolean connected;
+	gboolean ret = FALSE;
+	GcmClut *clut = NULL;
+	GcmXserver *xserver = NULL;
 	GnomeRRCrtc *crtc;
+	GnomeRROutput *output;
+	gint x, y;
+	gchar *profile = NULL;
+	gfloat gamma;
+	gfloat brightness;
+	gfloat contrast;
+	const gchar *output_name;
+	gchar *id = NULL;
 	guint size;
-	gboolean ret;
-	GcmClut *clut = NULL;
-	GError *error_local = NULL;
+	GcmDeviceType type;
+
+	/* get details about the device */
+	g_object_get (device,
+		      "id", &id,
+		      "type", &type,
+		      "profile", &profile,
+		      "gamma", &gamma,
+		      "brightness", &brightness,
+		      "contrast", &contrast,
+		      "native-device-xrandr", &output,
+		      NULL);
 
-	/* don't set the gamma if there is no device */
-	connected = gnome_rr_output_is_connected (output);
-	if (!connected) {
+	/* do no set the gamma for non-display types */
+	if (type != GCM_DEVICE_TYPE_DISPLAY) {
 		if (error != NULL)
-			*error = g_error_new (1, 0, "cannot get clut for a unconnected output");
+			*error = g_error_new (1, 0, "not a display: %s", id);
 		goto out;
 	}
 
-	/* get data */
-	name = gcm_utils_get_output_name (output);
-	egg_debug ("[%i] output %p (name=%s)", connected, output, name);
+	/* check we have an output */
+	if (output == NULL) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "no output for device: %s", id);
+		goto out;
+	}
 
-	/* get crtc */
+	/* get crtc size */
 	crtc = gnome_rr_output_get_crtc (output);
-
-	/* get gamma size */
 	size = gcm_utils_get_gamma_size (crtc, error);
 	if (size == 0)
 		goto out;
 
-	/* create a lookup table */
+	/* create CLUT */
 	clut = gcm_clut_new ();
-
-	/* set correct size */
 	g_object_set (clut,
+		      "profile", profile,
+		      "gamma", gamma,
+		      "brightness", brightness,
+		      "contrast", contrast,
 		      "size", size,
-		      "id", name,
 		      NULL);
 
-	/* lookup from config file */
-	ret = gcm_clut_load_from_config (clut, &error_local);
-	if (!ret) {
-		/* this is not fatal */
-		egg_debug ("failed to get values for %s: %s", name, error_local->message);
-		g_error_free (error_local);
-	}
-out:
-	g_free (name);
-	return clut;
-}
-
-/**
- * gcm_utils_set_output_gamma:
- *
- * Return value: %TRUE for success;
- **/
-gboolean
-gcm_utils_set_output_gamma (GnomeRROutput *output, GError **error)
-{
-	gboolean ret = FALSE;
-	GcmClut *clut = NULL;
-	GcmXserver *xserver = NULL;
-	GnomeRRCrtc *crtc;
-	gint x, y;
-	gchar *filename = NULL;
-	const gchar *output_name;
-
-	/* get CLUT */
-	clut = gcm_utils_get_clut_for_output (output, error);
-	if (clut == NULL)
+	/* load */
+	ret = gcm_clut_load_from_profile (clut, error);
+	if (!ret)
 		goto out;
 
-	/* get filename of the profile, or NULL */
-	g_object_get (clut,
-		      "profile", &filename,
-		      NULL);
-
-	/* get crtc */
-	crtc = gnome_rr_output_get_crtc (output);
-
 	/* actually set the gamma */
-	ret = gcm_utils_set_crtc_gamma (crtc, clut, error);
+	ret = gcm_utils_set_gamma_for_crtc (crtc, clut, error);
 	if (!ret)
 		goto out;
 
+	/* no profile to set */
+	if (profile == NULL)
+		goto out;
+
 	/* set the per-output profile atoms */
 	xserver = gcm_xserver_new ();
 	output_name = gnome_rr_output_get_name (output);
-	ret = gcm_xserver_set_output_profile (xserver, output_name, filename, error);
+	ret = gcm_xserver_set_output_profile (xserver, output_name, profile, error);
 	if (!ret)
 		goto out;
 
 	/* is the monitor our primary monitor */
 	gnome_rr_output_get_position (output, &x, &y);
-	if (x == 0 && y == 0 && filename != NULL) {
-		ret = gcm_xserver_set_root_window_profile (xserver, filename, error);
+	if (x == 0 && y == 0 && profile != NULL) {
+		ret = gcm_xserver_set_root_window_profile (xserver, profile, error);
 		if (!ret)
 			goto out;
 	}
 out:
-	g_free (filename);
+	g_free (id);
+	g_free (profile);
 	if (clut != NULL)
 		g_object_unref (clut);
 	if (xserver != NULL)
diff --git a/src/gcm-utils.h b/src/gcm-utils.h
index 87e727e..9ac5080 100644
--- a/src/gcm-utils.h
+++ b/src/gcm-utils.h
@@ -24,25 +24,12 @@
 
 #include <glib-object.h>
 
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include <libgnomeui/gnome-rr.h>
-
-#include <X11/extensions/Xrandr.h>
-
-#include "gcm-clut.h"
+#include "gcm-device.h"
 
 #define GCM_STOCK_ICON		"gnome-color-manager"
 #define GCM_PROFILE_PATH	"/.color/icc"
 
-gchar		*gcm_utils_get_output_name		(GnomeRROutput		*output);
-guint		 gcm_utils_get_gamma_size		(GnomeRRCrtc		*crtc,
-							 GError			**error);
-gboolean	 gcm_utils_set_crtc_gamma		(GnomeRRCrtc		*crtc,
-							 GcmClut		*clut,
-							 GError			**error);
-gboolean	 gcm_utils_set_output_gamma		(GnomeRROutput		*output,
-							 GError			**error);
-GcmClut		*gcm_utils_get_clut_for_output		(GnomeRROutput		*output,
+gboolean	 gcm_utils_set_gamma_for_device		(GcmDevice		*device,
 							 GError			**error);
 GPtrArray	*gcm_utils_get_profile_filenames	(void);
 gboolean	 gcm_utils_mkdir_and_copy		(const gchar		*source,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]