[gnome-color-manager: 2/80] Add a simple command gcm-ddc-util to be able to send custom DDC packets to a monitor in userspace
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-color-manager: 2/80] Add a simple command gcm-ddc-util to be able to send custom DDC packets to a monitor in userspace
- Date: Mon, 19 Jul 2010 11:32:37 +0000 (UTC)
commit b562e48d8a11d29802e28494c4e1f481ad664b62
Author: Richard Hughes <richard hughsie com>
Date: Sat Jul 17 22:27:01 2010 +0100
Add a simple command gcm-ddc-util to be able to send custom DDC packets to a monitor in userspace
contrib/reset.sh | 1 +
libcolor-glib/.gitignore | 16 +
libcolor-glib/Makefile.am | 16 +-
libcolor-glib/gcm-ddc-client.c | 342 +++++++++++++
libcolor-glib/gcm-ddc-client.h | 99 ++++
libcolor-glib/gcm-ddc-common.c | 171 +++++++
libcolor-glib/gcm-ddc-common.h | 59 +++
libcolor-glib/gcm-ddc-control.c | 446 +++++++++++++++++
libcolor-glib/gcm-ddc-control.h | 116 +++++
libcolor-glib/gcm-ddc-device.c | 1041 +++++++++++++++++++++++++++++++++++++++
libcolor-glib/gcm-ddc-device.h | 132 +++++
libcolor-glib/gcm-self-test.c | 26 +
libcolor-glib/libcolor-glib.h | 4 +
src/.gitignore | 1 +
src/Makefile.am | 16 +
src/gcm-ddc-util.c | 310 ++++++++++++
16 files changed, 2794 insertions(+), 2 deletions(-)
---
diff --git a/contrib/reset.sh b/contrib/reset.sh
new file mode 100755
index 0000000..01ea901
--- /dev/null
+++ b/contrib/reset.sh
@@ -0,0 +1 @@
+xrandr --output DVI1 --off && xrandr --output DVI1 --auto && xrandr --output DVI1 --right-of LVDS1
diff --git a/libcolor-glib/.gitignore b/libcolor-glib/.gitignore
new file mode 100644
index 0000000..a87d1f0
--- /dev/null
+++ b/libcolor-glib/.gitignore
@@ -0,0 +1,16 @@
+.deps
+.libs
+*.a
+*.o
+*.la
+*.lo
+*-marshal.c
+*-marshal.h
+*-self-test
+*.loT
+*.db
+*.sh
+*-version.h
+*.gir
+*.typelib
+*.pc
diff --git a/libcolor-glib/Makefile.am b/libcolor-glib/Makefile.am
index 1819424..b639dd0 100644
--- a/libcolor-glib/Makefile.am
+++ b/libcolor-glib/Makefile.am
@@ -29,14 +29,26 @@ libcolor_glib_includedir = $(includedir)/libcolor-glib
libcolor_glib_include_HEADERS = \
libcolor-glib.h \
- gcm-version.h \
gcm-common.h \
+ gcm-ddc-client.h \
+ gcm-ddc-device.h \
+ gcm-ddc-control.h \
+ gcm-ddc-common.h \
+ gcm-version.h \
$(NULL)
libcolor_glib_la_SOURCES = \
libcolor-glib.h \
- gcm-common.h \
gcm-common.c \
+ gcm-common.h \
+ gcm-ddc-client.c \
+ gcm-ddc-client.h \
+ gcm-ddc-device.c \
+ gcm-ddc-device.h \
+ gcm-ddc-control.c \
+ gcm-ddc-control.h \
+ gcm-ddc-common.c \
+ gcm-ddc-common.h \
gcm-version.h \
$(NULL)
diff --git a/libcolor-glib/gcm-ddc-client.c b/libcolor-glib/gcm-ddc-client.c
new file mode 100644
index 0000000..af79c85
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-client.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:gcm-ddc-client
+ * @short_description: For managing different i2c devices
+ *
+ * A GObject to use for accessing devices.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <stdlib.h>
+
+#include <gcm-ddc-client.h>
+#include <gcm-ddc-device.h>
+
+static void gcm_ddc_client_finalize (GObject *object);
+
+#define GCM_DDC_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DDC_CLIENT, GcmDdcClientPrivate))
+
+/**
+ * GcmDdcClientPrivate:
+ *
+ * Private #GcmDdcClient data
+ **/
+struct _GcmDdcClientPrivate
+{
+ GPtrArray *devices;
+ gboolean has_coldplug;
+ GcmVerbose verbose;
+};
+
+enum {
+ PROP_0,
+ PROP_HAS_COLDPLUG,
+ PROP_LAST
+};
+
+G_DEFINE_TYPE (GcmDdcClient, gcm_ddc_client, G_TYPE_OBJECT)
+
+/**
+ * gcm_ddc_client_ensure_coldplug:
+ **/
+static gboolean
+gcm_ddc_client_ensure_coldplug (GcmDdcClient *client, GError **error)
+{
+ gboolean ret = FALSE;
+ gboolean any_found = FALSE;
+ guint i;
+ gchar *filename;
+ GError *error_local = NULL;
+ GcmDdcDevice *device;
+
+ g_return_val_if_fail (GCM_IS_DDC_CLIENT(client), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* already done */
+ if (client->priv->has_coldplug)
+ return TRUE;
+
+ /* ensure we have the module loaded */
+ ret = g_file_test ("/sys/module/i2c_dev/srcversion", G_FILE_TEST_EXISTS);
+ if (!ret) {
+ g_set_error_literal (error, GCM_DDC_CLIENT_ERROR, GCM_DDC_CLIENT_ERROR_FAILED,
+ "unable to use I2C, you need to 'modprobe i2c-dev'");
+ goto out;
+ }
+
+ /* try each i2c port */
+ for (i=0; i<16; i++) {
+ filename = g_strdup_printf ("/dev/i2c-%i", i);
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_free (filename);
+ break;
+ }
+ device = gcm_ddc_device_new ();
+ gcm_ddc_device_set_verbose (device, client->priv->verbose);
+ ret = gcm_ddc_device_open (device, filename, &error_local);
+ if (!ret) {
+ if (client->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_warning ("failed to open %s: %s", filename, error_local->message);
+ g_clear_error (&error_local);
+ } else {
+ if (client->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_debug ("success, adding %s", filename);
+ any_found = TRUE;
+ g_ptr_array_add (client->priv->devices, g_object_ref (device));
+ }
+ g_object_unref (device);
+ g_free (filename);
+ }
+
+ /* nothing found */
+ if (!any_found) {
+ g_set_error_literal (error, GCM_DDC_CLIENT_ERROR, GCM_DDC_CLIENT_ERROR_FAILED,
+ "No devices found");
+ goto out;
+ }
+
+ /* success */
+ client->priv->has_coldplug = TRUE;
+out:
+ return any_found;
+}
+
+/**
+ * gcm_ddc_client_close:
+ **/
+gboolean
+gcm_ddc_client_close (GcmDdcClient *client, GError **error)
+{
+ guint i;
+ gboolean ret = TRUE;
+ GcmDdcDevice *device;
+
+ g_return_val_if_fail (GCM_IS_DDC_CLIENT(client), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* iterate each device */
+ for (i=0; i<client->priv->devices->len; i++) {
+ device = g_ptr_array_index (client->priv->devices, i);
+ ret = gcm_ddc_device_close (device, error);
+ if (!ret)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_client_get_devices:
+ **/
+GPtrArray *
+gcm_ddc_client_get_devices (GcmDdcClient *client, GError **error)
+{
+ gboolean ret;
+ GPtrArray *devices = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_CLIENT(client), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_client_ensure_coldplug (client, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ devices = g_ptr_array_ref (client->priv->devices);
+out:
+ return devices;
+}
+
+/**
+ * gcm_ddc_client_get_device_from_edid:
+ **/
+GcmDdcDevice *
+gcm_ddc_client_get_device_from_edid (GcmDdcClient *client, const gchar *edid_md5, GError **error)
+{
+ guint i;
+ gboolean ret;
+ const gchar *edid_md5_tmp;
+ GcmDdcDevice *device = NULL;
+ GcmDdcDevice *device_tmp;
+
+ g_return_val_if_fail (GCM_IS_DDC_CLIENT(client), NULL);
+ g_return_val_if_fail (edid_md5 != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_client_ensure_coldplug (client, error);
+ if (!ret)
+ goto out;
+
+ /* iterate each device */
+ for (i=0; i<client->priv->devices->len; i++) {
+ device_tmp = g_ptr_array_index (client->priv->devices, i);
+
+ /* get the md5 of the device */
+ edid_md5_tmp = gcm_ddc_device_get_edid_md5 (device_tmp, error);
+ if (edid_md5_tmp == NULL)
+ goto out;
+
+ /* matches? */
+ if (g_strcmp0 (edid_md5, edid_md5_tmp) == 0) {
+ device = device_tmp;
+ break;
+ }
+ }
+
+ /* failure */
+ if (device == NULL) {
+ g_set_error (error, GCM_DDC_CLIENT_ERROR, GCM_DDC_CLIENT_ERROR_FAILED,
+ "No devices found with edid %s", edid_md5);
+ }
+out:
+ return device;
+}
+
+/**
+ * gcm_ddc_client_set_verbose:
+ **/
+void
+gcm_ddc_client_set_verbose (GcmDdcClient *client, GcmVerbose verbose)
+{
+ g_return_if_fail (GCM_IS_DDC_CLIENT(client));
+ client->priv->verbose = verbose;
+}
+
+/**
+ * gcm_ddc_client_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+gcm_ddc_client_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("gcm_ddc_client_error");
+ return quark;
+}
+
+/**
+ * gcm_ddc_client_get_property:
+ **/
+static void
+gcm_ddc_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GcmDdcClient *client = GCM_DDC_CLIENT (object);
+ GcmDdcClientPrivate *priv = client->priv;
+
+ switch (prop_id) {
+ case PROP_HAS_COLDPLUG:
+ g_value_set_boolean (value, priv->has_coldplug);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_client_set_property:
+ **/
+static void
+gcm_ddc_client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_client_class_init:
+ **/
+static void
+gcm_ddc_client_class_init (GcmDdcClientClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gcm_ddc_client_finalize;
+ object_class->get_property = gcm_ddc_client_get_property;
+ object_class->set_property = gcm_ddc_client_set_property;
+
+ /**
+ * GcmDdcClient:has-coldplug:
+ *
+ * Since: 0.0.1
+ */
+ pspec = g_param_spec_boolean ("has-coldplug", NULL, "if there are no transactions in progress on this client",
+ TRUE,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_HAS_COLDPLUG, pspec);
+
+ g_type_class_add_private (klass, sizeof (GcmDdcClientPrivate));
+}
+
+/**
+ * gcm_ddc_client_init:
+ **/
+static void
+gcm_ddc_client_init (GcmDdcClient *client)
+{
+ client->priv = GCM_DDC_CLIENT_GET_PRIVATE (client);
+ client->priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+}
+
+/**
+ * gcm_ddc_client_finalize:
+ **/
+static void
+gcm_ddc_client_finalize (GObject *object)
+{
+ GcmDdcClient *client = GCM_DDC_CLIENT (object);
+ GcmDdcClientPrivate *priv = client->priv;
+
+ g_return_if_fail (GCM_IS_DDC_CLIENT(client));
+
+ g_ptr_array_unref (priv->devices);
+ G_OBJECT_CLASS (gcm_ddc_client_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_ddc_client_new:
+ *
+ * GcmDdcClient is a nice GObject wrapper for gcm.
+ *
+ * Return value: A new %GcmDdcClient instance
+ *
+ * Since: 0.0.1
+ **/
+GcmDdcClient *
+gcm_ddc_client_new (void)
+{
+ GcmDdcClient *client;
+ client = g_object_new (GCM_TYPE_DDC_CLIENT, NULL);
+ return GCM_DDC_CLIENT (client);
+}
diff --git a/libcolor-glib/gcm-ddc-client.h b/libcolor-glib/gcm-ddc-client.h
new file mode 100644
index 0000000..736f88c
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-client.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__GCM_H_INSIDE__) && !defined (GCM_COMPILATION)
+#error "Only <gcm.h> can be included directly."
+#endif
+
+#ifndef __GCM_DDC_CLIENT_H
+#define __GCM_DDC_CLIENT_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <gcm-ddc-common.h>
+#include <gcm-ddc-device.h>
+#include <gcm-ddc-client.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_DDC_CLIENT (gcm_ddc_client_get_type ())
+#define GCM_DDC_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DDC_CLIENT, GcmDdcClient))
+#define GCM_DDC_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DDC_CLIENT, GcmDdcClientClass))
+#define GCM_IS_DDC_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DDC_CLIENT))
+#define GCM_IS_DDC_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DDC_CLIENT))
+#define GCM_DDC_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DDC_CLIENT, GcmDdcClientClass))
+#define GCM_DDC_CLIENT_ERROR (gcm_ddc_client_error_quark ())
+#define GCM_DDC_CLIENT_TYPE_ERROR (gcm_ddc_client_error_get_type ())
+
+/**
+ * GcmDdcClientError:
+ * @GCM_DDC_CLIENT_ERROR_FAILED: the transaction failed for an unknown reason
+ *
+ * Errors that can be thrown
+ */
+typedef enum
+{
+ GCM_DDC_CLIENT_ERROR_FAILED
+} GcmDdcClientError;
+
+typedef struct _GcmDdcClientPrivate GcmDdcClientPrivate;
+typedef struct _GcmDdcClient GcmDdcClient;
+typedef struct _GcmDdcClientClass GcmDdcClientClass;
+
+struct _GcmDdcClient
+{
+ GObject parent;
+ GcmDdcClientPrivate *priv;
+};
+
+struct _GcmDdcClientClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+ void (* changed) (GcmDdcClient *client);
+ /* padding for future expansion */
+ void (*_gcm_reserved1) (void);
+ void (*_gcm_reserved2) (void);
+ void (*_gcm_reserved3) (void);
+ void (*_gcm_reserved4) (void);
+ void (*_gcm_reserved5) (void);
+};
+
+GQuark gcm_ddc_client_error_quark (void);
+GType gcm_ddc_client_get_type (void);
+GcmDdcClient *gcm_ddc_client_new (void);
+
+gboolean gcm_ddc_client_close (GcmDdcClient *client,
+ GError **error);
+GPtrArray *gcm_ddc_client_get_devices (GcmDdcClient *client,
+ GError **error);
+GcmDdcDevice *gcm_ddc_client_get_device_from_edid (GcmDdcClient *client,
+ const gchar *edid_md5,
+ GError **error);
+void gcm_ddc_client_set_verbose (GcmDdcClient *client,
+ GcmVerbose verbose);
+
+G_END_DECLS
+
+#endif /* __GCM_DDC_CLIENT_H */
+
diff --git a/libcolor-glib/gcm-ddc-common.c b/libcolor-glib/gcm-ddc-common.c
new file mode 100644
index 0000000..465a09a
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-common.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:gcm-ddc-common
+ * @short_description: Common functionality
+ *
+ * A GObject to use for common shizzle.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include <gcm-ddc-common.h>
+
+typedef struct {
+ guchar index;
+ const gchar *shortname;
+} GcmDescription;
+
+static const GcmDescription vcp_descriptions[] = {
+// { 0x00, "degauss" },
+ { 0x01, "degauss" },
+ { 0x02, "secondary-degauss" },
+ { 0x04, "reset-factory-defaults" },
+ { 0x05, "reset-brightness-and-contrast" },
+ { 0x06, "reset-factory-geometry" },
+ { 0x08, "reset-factory-default-color" },
+ { 0x0a, "reset-factory-default-position" },
+ { 0x0c, "reset-factory-default-size" },
+ { 0x0e, "image-lock-coarse" },
+ { 0x10, "brightness" },
+ { 0x12, "contrast" },
+ { 0x13, "backlight" },
+ { 0x14, "select-color-preset" },
+ { 0x16, "red-video-gain" },
+ { 0x18, "green-video-gain" },
+ { 0x1a, "blue-video-gain" },
+ { 0x1c, "hue" },
+ { 0x1e, "auto-size-center" },
+ { 0x20, "horizontal-position" },
+ { 0x22, "horizontal-size" },
+ { 0x24, "horizontal-pincushion" },
+ { 0x26, "horizontal-pincushion-balance" },
+ { 0x28, "horizontal-misconvergence" },
+ { 0x2a, "horizontal-linearity" },
+ { 0x2c, "horizontal-linearity-balance" },
+ { 0x30, "vertical-position" },
+ { 0x32, "vertical-size" },
+ { 0x34, "vertical-pincushion" },
+ { 0x36, "vertical-pincushion-balance" },
+ { 0x38, "vertical-misconvergence" },
+ { 0x3a, "vertical-linearity" },
+ { 0x3c, "vertical-linearity-balance" },
+ { 0x3e, "image-lock-fine" },
+ { 0x40, "parallelogram-distortion" },
+ { 0x42, "trapezoidal-distortion" },
+ { 0x44, "tilt" },
+ { 0x46, "top-corner-distortion-control" },
+ { 0x48, "top-corner-distortion-balance" },
+ { 0x4a, "bottom-corner-distortion-control" },
+ { 0x4c, "bottom-corner-distortion-balance" },
+ { 0x50, "hue" },
+ { 0x52, "saturation" },
+ { 0x54, "color-temp" },
+ { 0x56, "horizontal-moire" },
+ { 0x58, "vertical-moire" },
+ { 0x5a, "auto-size" },
+ { 0x5c, "landing-adjust" },
+ { 0x5e, "input-level-select" },
+ { 0x60, "input-source-select" },
+ { 0x62, "audio-speaker-volume-adjust" },
+ { 0x64, "audio-microphone-volume-adjust" },
+ { 0x66, "on-screen-displa" },
+ { 0x68, "language-select" },
+ { 0x6c, "red-video-black-level" },
+ { 0x6e, "green-video-black-level" },
+ { 0x70, "blue-video-black-level" },
+ { 0x8c, "sharpness" },
+ { 0x94, "mute" },
+ { 0xa2, "auto-size-center" },
+ { 0xa4, "polarity-horizontal-synchronization" },
+ { 0xa6, "polarity-vertical-synchronization" },
+ { 0xa8, "synchronization-type" },
+ { 0xaa, "screen-orientation" },
+ { 0xac, "horizontal-frequency" },
+ { 0xae, "vertical-frequency" },
+ { 0xb0, "settings" },
+ { 0xca, "on-screen-display" },
+ { 0xcc, "samsung-on-screen-display-language" },
+ { 0xc9, "firmware-version" },
+ { 0xd4, "stereo-mode" },
+ { 0xd6, "dpms-control-(1---on/4---stby)" },
+ { 0xdc, "magicbright-(1---text/2---internet/3---entertain/4---custom)" },
+ { 0xdf, "vcp-version" },
+ { 0xe0, "samsung-color-preset-(0---normal/1---warm/2---cool)" },
+ { 0xe1, "power-control-(0---off/1---on)" },
+ { 0xe2, "auto-source" },
+ { 0xe8, "tl-purity" },
+ { 0xe9, "tr-purity" },
+ { 0xea, "bl-purity" },
+ { 0xeb, "br-purity" },
+ { 0xed, "samsung-red-video-black-level" },
+ { 0xee, "samsung-green-video-black-level" },
+ { 0xef, "samsung-blue-video-black-level" },
+ { 0xf0, "magic-color" },
+ { 0xf1, "fe-brightness" },
+ { 0xf2, "fe-clarity / gamma" },
+ { 0xf3, "fe-color" },
+ { 0xf5, "samsung-osd" },
+ { 0xf6, "resolutionnotifier" },
+ { 0xf9, "super-bright" },
+ { 0xfc, "fe-mode" },
+ { 0xfd, "power-led" },
+ { GCM_VCP_ID_INVALID, NULL }
+ };
+
+/**
+ * gcm_get_vcp_description_from_index:
+ **/
+const gchar *
+gcm_get_vcp_description_from_index (guchar idx)
+{
+ guint i;
+
+ g_return_val_if_fail (idx != GCM_VCP_ID_INVALID, NULL);
+
+ for (i=0;;i++) {
+ if (vcp_descriptions[i].index == idx ||
+ vcp_descriptions[i].index == GCM_VCP_ID_INVALID)
+ break;
+ }
+ return vcp_descriptions[i].shortname;
+}
+
+/**
+ * gcm_get_vcp_index_from_description:
+ **/
+guchar
+gcm_get_vcp_index_from_description (const gchar *description)
+{
+ guint i;
+
+ g_return_val_if_fail (description != NULL, GCM_VCP_ID_INVALID);
+
+ for (i=0;;i++) {
+ if (g_strcmp0 (vcp_descriptions[i].shortname, description) == 0 ||
+ vcp_descriptions[i].shortname == NULL)
+ break;
+ }
+ return vcp_descriptions[i].index;
+}
diff --git a/libcolor-glib/gcm-ddc-common.h b/libcolor-glib/gcm-ddc-common.h
new file mode 100644
index 0000000..9aea474
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-common.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 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.
+ */
+
+#if !defined (__GCM_H_INSIDE__) && !defined (GCM_COMPILATION)
+#error "Only <gcm.h> can be included directly."
+#endif
+
+#ifndef __GCM_DDC_COMMON_H__
+#define __GCM_DDC_COMMON_H__
+
+#define __GCM_DDC_COMMON_H_INSIDE__
+
+typedef enum {
+ GCM_VERBOSE_NONE,
+ GCM_VERBOSE_OVERVIEW,
+ GCM_VERBOSE_PROTOCOL
+} GcmVerbose;
+
+#define GCM_VCP_REQUEST 0x01
+#define GCM_VCP_REPLY 0x02
+#define GCM_VCP_SET 0x03
+#define GCM_VCP_RESET 0x09
+#define GCM_DEFAULT_DDCCI_ADDR 0x37
+#define GCM_DEFAULT_EDID_ADDR 0x50
+#define GCM_CAPABILITIES_REQUEST 0xf3
+#define GCM_CAPABILITIES_REPLY 0xe3
+#define GCM_COMMAND_PRESENCE 0xf7
+#define GCM_ENABLE_APPLICATION_REPORT 0xf5
+
+#define GCM_VCP_ID_INVALID 0x00
+
+#define GCM_CTRL_DISABLE 0x0000
+#define GCM_CTRL_ENABLE 0x0001
+
+const gchar *gcm_get_vcp_description_from_index (guchar idx);
+guchar gcm_get_vcp_index_from_description (const gchar *description);
+
+#undef __GCM_DDC_COMMON_H_INSIDE__
+
+#endif /* __GCM_DDC_COMMON_H__ */
+
diff --git a/libcolor-glib/gcm-ddc-control.c b/libcolor-glib/gcm-ddc-control.c
new file mode 100644
index 0000000..3e51a31
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-control.c
@@ -0,0 +1,446 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:gcm-ddc-control
+ * @short_description: For managing different i2c controls
+ *
+ * A GObject to use for accessing controls.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib-object.h>
+
+#include <gcm-ddc-device.h>
+#include <gcm-ddc-control.h>
+
+static void gcm_ddc_control_finalize (GObject *object);
+
+#define GCM_DDC_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DDC_CONTROL, GcmDdcControlPrivate))
+
+/**
+ * GcmDdcControlPrivate:
+ *
+ * Private #GcmDdcControl data
+ **/
+struct _GcmDdcControlPrivate
+{
+ guchar id;
+ gboolean supported;
+ GcmDdcDevice *device;
+ GcmVerbose verbose;
+ GArray *values;
+};
+
+enum {
+ PROP_0,
+ PROP_SUPPORTED,
+ PROP_LAST
+};
+
+G_DEFINE_TYPE (GcmDdcControl, gcm_ddc_control, G_TYPE_OBJECT)
+
+#define GCM_VCP_SET_DELAY_USECS 50000
+
+/**
+ * gcm_ddc_control_get_description:
+ **/
+const gchar *
+gcm_ddc_control_get_description (GcmDdcControl *control)
+{
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), NULL);
+
+ return gcm_get_vcp_description_from_index (control->priv->id);
+}
+
+/**
+ * gcm_ddc_control_is_value_valid:
+ **/
+static gboolean
+gcm_ddc_control_is_value_valid (GcmDdcControl *control, guint16 value, GError **error)
+{
+ guint i;
+ gboolean ret = TRUE;
+ GArray *array;
+ GString *possible;
+
+ /* no data */
+ array = control->priv->values;
+ if (array->len == 0)
+ goto out;
+
+ /* see if it is present in the description */
+ for (i=0; i<array->len; i++) {
+ ret = (g_array_index (array, guint16, i) == value);
+ if (ret)
+ goto out;
+ }
+
+ /* not found */
+ if (!ret) {
+ possible = g_string_new ("");
+ for (i=0; i<array->len; i++)
+ g_string_append_printf (possible, "%i ", g_array_index (array, guint16, i));
+ g_set_error (error, GCM_DDC_CONTROL_ERROR, GCM_DDC_CONTROL_ERROR_FAILED,
+ "%i is not an allowed value for 0x%02x, possible values include %s",
+ value, control->priv->id, possible->str);
+ g_string_free (possible, TRUE);
+ }
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_control_set:
+ *
+ * write value to register ctrl of ddc/ci
+ **/
+gboolean
+gcm_ddc_control_set (GcmDdcControl *control, guint16 value, GError **error)
+{
+ gboolean ret = FALSE;
+ guchar buf[4];
+
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* check this value is allowed */
+ ret = gcm_ddc_control_is_value_valid (control, value, error);
+ if (!ret)
+ goto out;
+
+ buf[0] = GCM_VCP_SET;
+ buf[1] = control->priv->id;
+ buf[2] = (value >> 8);
+ buf[3] = (value & 255);
+
+ ret = gcm_ddc_device_write (control->priv->device, buf, sizeof(buf), error);
+ if (!ret)
+ goto out;
+
+ /* Do the delay */
+ g_usleep (GCM_VCP_SET_DELAY_USECS);
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_control_reset:
+ **/
+gboolean
+gcm_ddc_control_reset (GcmDdcControl *control, GError **error)
+{
+ gboolean ret;
+ guchar buf[2];
+
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ buf[0] = GCM_VCP_RESET;
+ buf[1] = control->priv->id;
+
+ ret = gcm_ddc_device_write (control->priv->device, buf, sizeof(buf), error);
+ if (!ret)
+ goto out;
+
+ /* Do the delay */
+ g_usleep (GCM_VCP_SET_DELAY_USECS);
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_control_request:
+ **/
+gboolean
+gcm_ddc_control_request (GcmDdcControl *control, guint16 *value, guint16 *maximum, GError **error)
+{
+ gboolean ret = FALSE;
+ guchar buf[8];
+ gsize len;
+
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* request data */
+ buf[0] = GCM_VCP_REQUEST;
+ buf[1] = control->priv->id;
+ if (!gcm_ddc_device_write (control->priv->device, buf, 2, error))
+ goto out;
+
+ /* get data */
+ ret = gcm_ddc_device_read (control->priv->device, buf, 8, &len, error);
+ if (!ret)
+ goto out;
+
+ /* check we got enough data */
+ if (len != sizeof(buf)) {
+ g_set_error (error, GCM_DDC_CONTROL_ERROR, GCM_DDC_CONTROL_ERROR_FAILED,
+ "Failed to parse control 0x%02x as incorrect length", control->priv->id);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* message type incorrect */
+ if (buf[0] != GCM_VCP_REPLY) {
+ g_set_error (error, GCM_DDC_CONTROL_ERROR, GCM_DDC_CONTROL_ERROR_FAILED,
+ "Failed to parse control 0x%02x as incorrect command returned", control->priv->id);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* ensure the control is supported by the display */
+ if (buf[1] != 0) {
+ g_set_error (error, GCM_DDC_CONTROL_ERROR, GCM_DDC_CONTROL_ERROR_FAILED,
+ "Failed to parse control 0x%02x as unsupported", control->priv->id);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* check we are getting the correct control */
+ if (buf[2] != control->priv->id) {
+ g_set_error (error, GCM_DDC_CONTROL_ERROR, GCM_DDC_CONTROL_ERROR_FAILED,
+ "Failed to parse control 0x%02x as incorrect id returned", control->priv->id);
+ ret = FALSE;
+ goto out;
+ }
+
+ if (value != NULL)
+ *value = buf[6] * 256 + buf[7];
+ if (maximum != NULL)
+ *maximum = buf[4] * 256 + buf[5];
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_control_run:
+ **/
+gboolean
+gcm_ddc_control_run (GcmDdcControl *control, GError **error)
+{
+ guchar buf[1];
+
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ buf[0] = control->priv->id;
+ return gcm_ddc_device_write (control->priv->device, buf, sizeof(buf), error);
+}
+
+/**
+ * gcm_ddc_control_parse:
+ **/
+void
+gcm_ddc_control_parse (GcmDdcControl *control, guchar id, const gchar *values)
+{
+ guint i;
+ guint16 value;
+ gchar **split = NULL;
+
+ g_return_if_fail (GCM_IS_DDC_CONTROL(control));
+
+ /* just save this */
+ control->priv->id = id;
+ if (control->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_debug ("add control 0x%02x (%s)", id, gcm_ddc_control_get_description (control));
+
+ /* do we have any values to parse */
+ if (values == NULL)
+ goto out;
+
+ /* tokenize */
+ split = g_strsplit (values, " ", -1);
+ for (i=0; split[i] != NULL; i++) {
+ value = atoi (split[i]);
+ if (control->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_debug ("add value %i to control 0x%02x", value, id);
+ g_array_append_val (control->priv->values, value);
+ }
+out:
+ g_strfreev (split);
+}
+
+/**
+ * gcm_ddc_control_set_device:
+ **/
+void
+gcm_ddc_control_set_device (GcmDdcControl *control, GcmDdcDevice *device)
+{
+ g_return_if_fail (GCM_IS_DDC_CONTROL(control));
+ g_return_if_fail (GCM_IS_DDC_DEVICE(device));
+ control->priv->device = g_object_ref (device);
+}
+
+/**
+ * gcm_ddc_control_get_id:
+ **/
+guchar
+gcm_ddc_control_get_id (GcmDdcControl *control)
+{
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), 0);
+
+ return control->priv->id;
+}
+
+/**
+ * gcm_ddc_control_get_values:
+ *
+ * Return value: an array of guint16
+ **/
+GArray *
+gcm_ddc_control_get_values (GcmDdcControl *control)
+{
+ g_return_val_if_fail (GCM_IS_DDC_CONTROL(control), NULL);
+
+ return g_array_ref (control->priv->values);
+}
+
+/**
+ * gcm_ddc_control_set_verbose:
+ **/
+void
+gcm_ddc_control_set_verbose (GcmDdcControl *control, GcmVerbose verbose)
+{
+ g_return_if_fail (GCM_IS_DDC_CONTROL(control));
+ control->priv->verbose = verbose;
+}
+
+/**
+ * gcm_ddc_control_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+gcm_ddc_control_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("gcm_ddc_control_error");
+ return quark;
+}
+
+/**
+ * gcm_ddc_control_get_property:
+ **/
+static void
+gcm_ddc_control_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GcmDdcControl *control = GCM_DDC_CONTROL (object);
+ GcmDdcControlPrivate *priv = control->priv;
+
+ switch (prop_id) {
+ case PROP_SUPPORTED:
+ g_value_set_boolean (value, priv->supported);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_control_set_property:
+ **/
+static void
+gcm_ddc_control_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_control_class_init:
+ **/
+static void
+gcm_ddc_control_class_init (GcmDdcControlClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gcm_ddc_control_finalize;
+ object_class->get_property = gcm_ddc_control_get_property;
+ object_class->set_property = gcm_ddc_control_set_property;
+
+ /**
+ * GcmDdcControl:supported:
+ *
+ * Since: 0.0.1
+ */
+ pspec = g_param_spec_boolean ("supported", NULL, "if there are no transactions in progress on this control",
+ TRUE,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_SUPPORTED, pspec);
+
+ g_type_class_add_private (klass, sizeof (GcmDdcControlPrivate));
+}
+
+/**
+ * gcm_ddc_control_init:
+ **/
+static void
+gcm_ddc_control_init (GcmDdcControl *control)
+{
+ control->priv = GCM_DDC_CONTROL_GET_PRIVATE (control);
+ control->priv->id = 0xff;
+ control->priv->values = g_array_new (FALSE, FALSE, sizeof(guint16));
+}
+
+/**
+ * gcm_ddc_control_finalize:
+ **/
+static void
+gcm_ddc_control_finalize (GObject *object)
+{
+ GcmDdcControl *control = GCM_DDC_CONTROL (object);
+ GcmDdcControlPrivate *priv = control->priv;
+
+ g_return_if_fail (GCM_IS_DDC_CONTROL(control));
+
+ g_array_free (priv->values, TRUE);
+ if (priv->device != NULL)
+ g_object_unref (priv->device);
+
+ G_OBJECT_CLASS (gcm_ddc_control_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_ddc_control_new:
+ *
+ * GcmDdcControl is a nice GObject wrapper for gcm.
+ *
+ * Return value: A new %GcmDdcControl instance
+ *
+ * Since: 0.0.1
+ **/
+GcmDdcControl *
+gcm_ddc_control_new (void)
+{
+ GcmDdcControl *control;
+ control = g_object_new (GCM_TYPE_DDC_CONTROL, NULL);
+ return GCM_DDC_CONTROL (control);
+}
diff --git a/libcolor-glib/gcm-ddc-control.h b/libcolor-glib/gcm-ddc-control.h
new file mode 100644
index 0000000..eb7e036
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-control.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__GCM_H_INSIDE__) && !defined (GCM_COMPILATION)
+#error "Only <gcm.h> can be included directly."
+#endif
+
+#ifndef __GCM_DDC_CONTROL_H
+#define __GCM_DDC_CONTROL_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <gcm-ddc-common.h>
+#include <gcm-ddc-control.h>
+#include <gcm-ddc-device.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_DDC_CONTROL (gcm_ddc_control_get_type ())
+#define GCM_DDC_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DDC_CONTROL, GcmDdcControl))
+#define GCM_DDC_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DDC_CONTROL, GcmDdcControlClass))
+#define GCM_IS_DDC_CONTROL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DDC_CONTROL))
+#define GCM_IS_DDC_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DDC_CONTROL))
+#define GCM_DDC_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DDC_CONTROL, GcmDdcControlClass))
+#define GCM_DDC_CONTROL_ERROR (gcm_ddc_control_error_quark ())
+#define GCM_DDC_CONTROL_TYPE_ERROR (gcm_ddc_control_error_get_type ())
+
+/**
+ * GcmDdcControlError:
+ * @GCM_DDC_CONTROL_ERROR_FAILED: the transaction failed for an unknown reason
+ *
+ * Errors that can be thrown
+ */
+typedef enum
+{
+ GCM_DDC_CONTROL_ERROR_FAILED
+} GcmDdcControlError;
+
+typedef struct _GcmDdcControlPrivate GcmDdcControlPrivate;
+typedef struct _GcmDdcControl GcmDdcControl;
+typedef struct _GcmDdcControlClass GcmDdcControlClass;
+
+struct _GcmDdcControl
+{
+ GObject parent;
+ GcmDdcControlPrivate *priv;
+};
+
+struct _GcmDdcControlClass
+{
+ 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 struct {
+ guint id;
+ GPtrArray *int_values;
+} GcmDdcControlCap;
+
+/* control numbers */
+#define GCM_DDC_CONTROL_ID_BRIGHTNESS 0x10
+
+GQuark gcm_ddc_control_error_quark (void);
+GType gcm_ddc_control_get_type (void);
+GcmDdcControl *gcm_ddc_control_new (void);
+
+void gcm_ddc_control_parse (GcmDdcControl *control,
+ guchar id,
+ const gchar *values);
+void gcm_ddc_control_set_device (GcmDdcControl *control,
+ GcmDdcDevice *device);
+void gcm_ddc_control_set_verbose (GcmDdcControl *control,
+ GcmVerbose verbose);
+gboolean gcm_ddc_control_run (GcmDdcControl *control,
+ GError **error);
+gboolean gcm_ddc_control_request (GcmDdcControl *control,
+ guint16 *value,
+ guint16 *maximum,
+ GError **error);
+gboolean gcm_ddc_control_set (GcmDdcControl *control,
+ guint16 value,
+ GError **error);
+gboolean gcm_ddc_control_reset (GcmDdcControl *control,
+ GError **error);
+guchar gcm_ddc_control_get_id (GcmDdcControl *control);
+const gchar *gcm_ddc_control_get_description (GcmDdcControl *control);
+GArray *gcm_ddc_control_get_values (GcmDdcControl *control);
+
+G_END_DECLS
+
+#endif /* __GCM_DDC_CONTROL_H */
+
diff --git a/libcolor-glib/gcm-ddc-device.c b/libcolor-glib/gcm-ddc-device.c
new file mode 100644
index 0000000..912c04a
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-device.c
@@ -0,0 +1,1041 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:gcm-ddc-device
+ * @short_description: For managing different i2c devices
+ *
+ * A GObject to use for accessing devices.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#include <gcm-ddc-device.h>
+#include <gcm-ddc-control.h>
+
+static void gcm_ddc_device_finalize (GObject *object);
+
+#define GCM_DDC_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DDC_DEVICE, GcmDdcDevicePrivate))
+
+/* ddc/ci iface tunables */
+#define GCM_MAX_MESSAGE_BYTES 127
+#define GCM_READ_DELAY_SECS 0.04f
+#define GCM_WRITE_DELAY_SECS 0.05f
+
+#define GCM_SAVE_CURRENT_SETTINGS 0x0c
+#define GCM_SAVE_DELAY_USECS 200000
+
+/* magic numbers */
+#define GCM_MAGIC_BYTE1 0x51 /* host address */
+#define GCM_MAGIC_BYTE2 0x80 /* ored with length */
+#define GCM_MAGIC_XOR 0x50 /* initial xor for received frame */
+
+/**
+ * GcmDdcDevicePrivate:
+ *
+ * Private #GcmDdcDevice data
+ **/
+struct _GcmDdcDevicePrivate
+{
+ gint fd;
+ guint addr;
+ GcmDdcDeviceKind kind;
+ gchar *model;
+ gchar *pnpid;
+ guint8 *edid_data;
+ gsize edid_length;
+ gchar *edid_md5;
+ GPtrArray *controls;
+ gboolean has_controls;
+ gboolean has_edid;
+ gdouble required_wait;
+ GTimer *timer;
+ GcmVerbose verbose;
+};
+
+enum {
+ PROP_0,
+ PROP_HAS_EDID,
+ PROP_LAST
+};
+
+G_DEFINE_TYPE (GcmDdcDevice, gcm_ddc_device, G_TYPE_OBJECT)
+
+/**
+ * gcm_ddc_device_print_hex_data:
+ **/
+static void
+gcm_ddc_device_print_hex_data (const gchar *text, const guchar *data, gsize length)
+{
+ guint i;
+
+ g_print ("%s: ", text);
+ for (i=0; i<length; i++) {
+ if (!g_ascii_isprint (data[i]))
+ g_print ("%02x [?] ", data[i]);
+ else
+ g_print ("%02x [%c] ", data[i], data[i]);
+ if (i % 8 == 7)
+ g_print ("\n ");
+ }
+ g_print ("\n");
+}
+
+/**
+ * gcm_ddc_device_set_required_wait:
+ **/
+static void
+gcm_ddc_device_set_required_wait (GcmDdcDevice *device, gdouble delay)
+{
+ device->priv->required_wait = delay;
+}
+
+/**
+ * gcm_ddc_device_i2c_write:
+ **/
+static gboolean
+gcm_ddc_device_i2c_write (GcmDdcDevice *device, guint addr, const guchar *data, gsize length, GError **error)
+{
+ gint i;
+ struct i2c_rdwr_ioctl_data msg_rdwr;
+ struct i2c_msg i2cmsg;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* done, prepare message */
+ msg_rdwr.msgs = &i2cmsg;
+ msg_rdwr.nmsgs = 1;
+
+ i2cmsg.addr = addr;
+ i2cmsg.flags = 0;
+ i2cmsg.len = length;
+ i2cmsg.buf = (unsigned char *) data;
+
+ /* hit hardware */
+ i = ioctl (device->priv->fd, I2C_RDWR, &msg_rdwr);
+ if (i < 0 ) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "ioctl returned %d", i);
+ return FALSE;
+ }
+
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ gcm_ddc_device_print_hex_data ("Send", data, length);
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_i2c_read:
+ **/
+static gboolean
+gcm_ddc_device_i2c_read (GcmDdcDevice *device, guint addr, guchar *data, gsize data_length, gsize *recieved_length, GError **error)
+{
+ struct i2c_rdwr_ioctl_data msg_rdwr;
+ struct i2c_msg i2cmsg;
+ gint i;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ msg_rdwr.msgs = &i2cmsg;
+ msg_rdwr.nmsgs = 1;
+
+ i2cmsg.addr = addr;
+ i2cmsg.flags = I2C_M_RD;
+ i2cmsg.len = data_length;
+ i2cmsg.buf = data;
+
+ /* hit hardware */
+ i = ioctl (device->priv->fd, I2C_RDWR, &msg_rdwr);
+ if (i < 0) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "ioctl returned %d", i);
+ return FALSE;
+ }
+
+ if (recieved_length != NULL)
+ *recieved_length = i2cmsg.len;
+
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ gcm_ddc_device_print_hex_data ("Recv", data, i2cmsg.len);
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_edid_valid:
+ **/
+static gboolean
+gcm_ddc_device_edid_valid (const guint8 *data, gsize length)
+{
+ if (length < 0x0f)
+ return FALSE;
+ if (data[0] != 0 || data[1] != 0xff || data[2] != 0xff || data[3] != 0xff ||
+ data[4] != 0xff || data[5] != 0xff || data[6] != 0xff || data[7] != 0) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_ensure_edid:
+ **/
+static gboolean
+gcm_ddc_device_ensure_edid (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret = FALSE;
+ GError *error_local = NULL;
+ gint addr = GCM_DEFAULT_EDID_ADDR;
+ guchar buf[128];
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* already done */
+ if (device->priv->has_edid)
+ return TRUE;
+
+ /* send edid with offset zero */
+ buf[0] = 0;
+ if (!gcm_ddc_device_i2c_write (device, addr, buf, 1, &error_local)) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "failed to request EDID: %s", error_local->message);
+ g_error_free (error_local);
+ goto out;
+ }
+
+ /* read out data */
+ device->priv->edid_data = g_new0 (guint8, 128);
+ if (!gcm_ddc_device_i2c_read (device, addr, device->priv->edid_data, 128, &device->priv->edid_length, &error_local)) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "failed to recieve EDID: %s", error_local->message);
+ g_error_free (error_local);
+ goto out;
+ }
+
+ /* check valid */
+ ret = gcm_ddc_device_edid_valid (device->priv->edid_data, device->priv->edid_length);
+ if (!ret) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "corrupted EDID at 0x%02x", addr);
+ goto out;
+ }
+
+ /* get md5 hash */
+ device->priv->edid_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ device->priv->edid_data,
+ device->priv->edid_length);
+
+ /* print */
+ device->priv->pnpid = g_strdup_printf ("%c%c%c%02X%02X",
+ ((device->priv->edid_data[8] >> 2) & 31) + 'A' - 1,
+ ((device->priv->edid_data[8] & 3) << 3) + (device->priv->edid_data[9] >> 5) + 'A' - 1,
+ (device->priv->edid_data[9] & 31) + 'A' - 1, device->priv->edid_data[11], device->priv->edid_data[10]);
+ device->priv->has_edid = TRUE;
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_wait_for_hardware:
+ *
+ * Stalls execution, allowing write transaction to complete
+ **/
+static void
+gcm_ddc_device_wait_for_hardware (GcmDdcDevice *device)
+{
+ gdouble elapsed;
+ GcmDdcDevicePrivate *priv = device->priv;
+
+ /* only wait if enough time hasn't yet passed */
+ elapsed = g_timer_elapsed (priv->timer, NULL);
+ if (elapsed < priv->required_wait)
+ g_usleep ((priv->required_wait - elapsed) * G_USEC_PER_SEC);
+ g_timer_reset (priv->timer);
+}
+
+/**
+ * gcm_ddc_device_write:
+ *
+ * write data to ddc/ci at address addr
+ **/
+gboolean
+gcm_ddc_device_write (GcmDdcDevice *device, guchar *data, gsize length, GError **error)
+{
+ gint i = 0;
+ guchar buf[GCM_MAX_MESSAGE_BYTES + 3];
+ unsigned xor;
+ gboolean ret;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* initial xor value */
+ xor = ((guchar)device->priv->addr << 1);
+
+ /* put first magic */
+ xor ^= (buf[i++] = GCM_MAGIC_BYTE1);
+
+ /* second magic includes message size */
+ xor ^= (buf[i++] = GCM_MAGIC_BYTE2 | length);
+
+ /* bytes to send */
+ while (length--)
+ xor ^= (buf[i++] = *data++);
+
+ /* finally put checksum */
+ buf[i++] = xor;
+
+ /* wait for previous write to complete */
+ gcm_ddc_device_wait_for_hardware (device);
+
+ /* write to device */
+ ret = gcm_ddc_device_i2c_write (device, device->priv->addr, buf, i, error);
+ if (!ret)
+ goto out;
+
+ /* we have to wait at least this much time before submitting another command */
+ gcm_ddc_device_set_required_wait (device, GCM_WRITE_DELAY_SECS);
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_read:
+ *
+ * Read ddc/ci formatted frame from ddc/ci
+ **/
+gboolean
+gcm_ddc_device_read (GcmDdcDevice *device, guchar *data, gsize data_length, gsize *recieved_length, GError **error)
+{
+ guchar buf[GCM_MAX_MESSAGE_BYTES];
+ guchar xor = GCM_MAGIC_XOR;
+ guint i;
+ gsize len;
+ gboolean ret;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* wait for previous write to complete */
+ gcm_ddc_device_wait_for_hardware (device);
+
+ /* get data */
+ ret = gcm_ddc_device_i2c_read (device, device->priv->addr, buf, data_length + 3, recieved_length, error);
+ if (!ret)
+ goto out;
+
+ /* validate answer */
+ if (buf[0] != device->priv->addr * 2) { /* busy ??? */
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "Invalid response, first byte is 0x%02x, should be 0x%02x",
+ buf[0], device->priv->addr * 2);
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ gcm_ddc_device_print_hex_data ("Bugz", buf, data_length + 3);
+ ret = FALSE;
+ goto out;
+ }
+
+ if ((buf[1] & GCM_MAGIC_BYTE2) == 0) {
+ /* Fujitsu Siemens P19-2 and NEC LCD 1970NX send wrong magic when reading caps. */
+ g_debug ( "Invalid response, magic is 0x%02x, correcting", buf[1]);
+ }
+
+ len = buf[1] & ~GCM_MAGIC_BYTE2;
+ if (len > data_length || len > sizeof(buf)) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "Invalid response, length is %d, should be %d at most",
+ len, data_length);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* get the xor value */
+ for (i = 0; i < len + 3; i++)
+ xor ^= buf[i];
+ if (xor != 0) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "Invalid response, corrupted data - xor is 0x%02x, length 0x%02x", xor, len);
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ gcm_ddc_device_print_hex_data ("Bugz", buf, data_length + 3);
+ ret = FALSE;
+ goto out;
+ }
+
+ /* copy payload data */
+ memcpy (data, buf + 2, len);
+ if (recieved_length != NULL)
+ *recieved_length = len;
+
+ /* we have to wait at least this much time before reading the results */
+ gcm_ddc_device_set_required_wait (device, GCM_READ_DELAY_SECS);
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_capabilities_request:
+ *
+ * read capabilities raw data of ddc/ci
+ **/
+static gint
+gcm_ddc_device_capabilities_request (GcmDdcDevice *device, guint offset, guchar *data, gsize data_length, gsize *recieved_length, GError **error)
+{
+ guchar buf[3];
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ buf[0] = GCM_CAPABILITIES_REQUEST;
+ buf[1] = offset >> 8;
+ buf[2] = offset & 255;
+
+ if (!gcm_ddc_device_write (device, buf, sizeof(buf), error))
+ return FALSE;
+
+ return gcm_ddc_device_read (device, data, data_length, recieved_length, error);
+}
+
+/**
+ * gcm_ddc_device_add_control:
+ **/
+static gboolean
+gcm_ddc_device_add_control (GcmDdcDevice *device, const gchar *index_str, const gchar *controls_str)
+{
+ GcmDdcControl *control;
+ control = gcm_ddc_control_new ();
+ gcm_ddc_control_set_verbose (control, device->priv->verbose);
+ gcm_ddc_control_set_device (control, device);
+ gcm_ddc_control_parse (control, strtoul (index_str, NULL, 16), controls_str);
+ g_ptr_array_add (device->priv->controls, control);
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_set_device_property:
+ **/
+static gboolean
+gcm_ddc_device_set_device_property (GcmDdcDevice *device, const gchar *key, const gchar *value)
+{
+ if (device->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_debug ("key=%s, value=%s", key, value);
+ if (g_strcmp0 (key, "type") == 0) {
+ if (g_strcmp0 (value, "lcd") == 0)
+ device->priv->kind = GCM_DDC_DEVICE_KIND_LCD;
+ else if (g_strcmp0 (value, "crt") == 0)
+ device->priv->kind = GCM_DDC_DEVICE_KIND_CRT;
+ } else if (g_strcmp0 (key, "model") == 0)
+ device->priv->model = g_strdup (value);
+ else if (g_strcmp0 (key, "vcp") == 0) {
+ guint i;
+ gchar *tmp;
+ gchar *index_str;
+ gchar *caps_str = NULL;
+ guint refcount = 0;
+
+ tmp = g_strdup (value);
+ index_str = tmp;
+ for (i=0; tmp[i] != '\0'; i++) {
+ if (tmp[i] == '(') {
+ tmp[i] = '\0';
+ refcount++;
+ caps_str = &tmp[i] + 1;
+ } else if (tmp[i] == ')') {
+ tmp[i] = '\0';
+ refcount--;
+ } else if (tmp[i] == ' ' && refcount == 0) {
+ tmp[i] = '\0';
+ gcm_ddc_device_add_control (device, index_str, caps_str);
+ index_str = &tmp[i] + 1;
+ caps_str = NULL;
+ }
+ }
+ gcm_ddc_device_add_control (device, index_str, caps_str);
+ }
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_parse_caps:
+ **/
+static gboolean
+gcm_ddc_device_parse_caps (GcmDdcDevice *device, const gchar *caps)
+{
+ guint i;
+ gchar *tmp = g_strdup (caps);
+ gchar *key = tmp+1;
+ gchar *value = NULL;
+ guint refcount = 0;
+
+ /* decode string */
+ for (i=1; tmp[i] != '\0'; i++) {
+ if (tmp[i] == '(') {
+ if (refcount++ == 0) {
+ tmp[i] = '\0';
+ value = &tmp[i+1];
+ }
+ } else if (tmp[i] == ')') {
+ if (--refcount == 0) {
+ tmp[i] = '\0';
+ gcm_ddc_device_set_device_property (device, key, value);
+ key = &tmp[i]+1;
+ }
+ }
+ }
+
+ g_free (tmp);
+ return TRUE;
+}
+
+/**
+ * gcm_ddc_device_ensure_controls:
+ **/
+static gboolean
+gcm_ddc_device_ensure_controls (GcmDdcDevice *device, GError **error)
+{
+ guchar buf[64];
+ gint offset = 0;
+ gsize len;
+ gint retries = 5;
+ GString *string;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* already done */
+ if (device->priv->has_controls)
+ return TRUE;
+
+ /* allocate space for the controls */
+ string = g_string_new ("");
+ do {
+ /* we're shit out of luck, Brian */
+ if (retries == 0)
+ goto out;
+
+ /* clear previous error */
+ g_clear_error (error);
+
+ /* try to read */
+ ret = gcm_ddc_device_capabilities_request (device, offset, buf, sizeof(buf), &len, error);
+ if (!ret) {
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ g_warning ("Failed to read capabilities offset 0x%02x.", offset);
+ retries--;
+ continue;
+ }
+
+ /* not enough data */
+ if (len < 3) {
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ g_warning ("Not enough capabilities data at offset 0x%02x.", offset);
+ retries--;
+ continue;
+ }
+
+ /* check response */
+ if (buf[0] != GCM_CAPABILITIES_REPLY) {
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ g_warning ("Not correct capabilities reply at offset 0x%02x.", offset);
+ retries--;
+ continue;
+ }
+
+ /* check offset */
+ if ((buf[1] * 256 + buf[2]) != offset) {
+ if (device->priv->verbose == GCM_VERBOSE_PROTOCOL)
+ g_warning ("Not correct capabilities offset at offset 0x%02x.", offset);
+ retries--;
+ continue;
+ }
+
+ /* add to results */
+ g_string_append_len (string, (const gchar *) buf + 3, len - 3);
+ offset += len - 3;
+ retries = 3;
+ } while (len != 3);
+
+ if (device->priv->verbose == GCM_VERBOSE_OVERVIEW)
+ g_debug ("raw caps: %s", string->str);
+
+ /* parse */
+ ret = gcm_ddc_device_parse_caps (device, string->str);
+ if (!ret) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "failed to parse caps");
+ goto out;
+ }
+
+ /* success */
+ device->priv->has_controls = TRUE;
+out:
+ g_string_free (string, TRUE);
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_save:
+ **/
+gboolean
+gcm_ddc_device_save (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret = FALSE;
+ GcmDdcControl *control;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get control */
+ control = gcm_ddc_device_get_control_by_id (device, GCM_SAVE_CURRENT_SETTINGS, error);
+ if (control == NULL)
+ goto out;
+
+ /* run it */
+ ret = gcm_ddc_control_run (control, error);
+ if (!ret)
+ goto out;
+
+ /* super long delay to allow for saving to eeprom */
+ g_usleep (GCM_SAVE_DELAY_USECS);
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_startup:
+ **/
+static gboolean
+gcm_ddc_device_startup (GcmDdcDevice *device, GError **error)
+{
+ GcmDdcControl *control;
+ gboolean ret = FALSE;
+ if (device->priv->pnpid != NULL && g_str_has_prefix (device->priv->pnpid, "SAM")) {
+ control = gcm_ddc_device_get_control_by_id (device, GCM_ENABLE_APPLICATION_REPORT, error);
+ if (control == NULL)
+ goto out;
+ ret = gcm_ddc_control_set (control, GCM_CTRL_ENABLE, error);
+ } else {
+ /* this is not fatal if it's not found */
+ control = gcm_ddc_device_get_control_by_id (device, GCM_COMMAND_PRESENCE, NULL);
+ if (control == NULL) {
+ ret = TRUE;
+ goto out;
+ }
+ ret = gcm_ddc_control_run (control, error);
+ }
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_close:
+ **/
+gboolean
+gcm_ddc_device_close (GcmDdcDevice *device, GError **error)
+{
+ GcmDdcControl *control;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (device->priv->pnpid != NULL && g_str_has_prefix (device->priv->pnpid, "SAM")) {
+ control = gcm_ddc_device_get_control_by_id (device, GCM_ENABLE_APPLICATION_REPORT, error);
+ if (control == NULL)
+ goto out;
+ ret = gcm_ddc_control_set (control, GCM_CTRL_DISABLE, error);
+ } else {
+ ret = TRUE;
+ }
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_open:
+ **/
+gboolean
+gcm_ddc_device_open (GcmDdcDevice *device, const gchar *filename, GError **error)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* ensure we have the module loaded */
+ ret = g_file_test ("/sys/module/i2c_dev/srcversion", G_FILE_TEST_EXISTS);
+ if (!ret) {
+ g_set_error_literal (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "unable to use I2C, you need to 'modprobe i2c-dev'");
+ goto out;
+ }
+
+ /* open file */
+ device->priv->fd = open (filename, O_RDWR);
+ if (device->priv->fd < 0) {
+ ret = FALSE;
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "failed to open: %i", device->priv->fd);
+ goto out;
+ }
+
+ /* enable interface (need edid for pnpid) */
+ ret = gcm_ddc_device_ensure_edid (device, error);
+ if (!ret)
+ goto out;
+
+ /* startup for samsung mode */
+ ret = gcm_ddc_device_startup (device, error);
+ if (!ret)
+ goto out;
+out:
+ return ret;
+}
+
+/**
+ * gcm_ddc_device_get_controls:
+ **/
+GPtrArray *
+gcm_ddc_device_get_controls (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret;
+ GPtrArray *controls = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_controls (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ controls = g_ptr_array_ref (device->priv->controls);
+out:
+ return controls;
+}
+
+/**
+ * gcm_ddc_device_get_control_by_id:
+ **/
+GcmDdcControl *
+gcm_ddc_device_get_control_by_id (GcmDdcDevice *device, guchar id, GError **error)
+{
+ guint i;
+ gboolean ret;
+ GcmDdcControl *control = NULL;
+ GcmDdcControl *control_tmp = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_controls (device, error);
+ if (!ret)
+ goto out;
+
+ /* search each control */
+ for (i=0; i<device->priv->controls->len; i++) {
+ control_tmp = g_ptr_array_index (device->priv->controls, i);
+ if (gcm_ddc_control_get_id (control_tmp) == id) {
+ control = g_object_ref (control_tmp);
+ break;
+ }
+ }
+
+ /* failure */
+ if (control == NULL) {
+ g_set_error (error, GCM_DDC_DEVICE_ERROR, GCM_DDC_DEVICE_ERROR_FAILED,
+ "could not find a control id 0x%02x", (guint) id);
+ }
+out:
+ return control;
+}
+
+/**
+ * gcm_ddc_device_get_edid:
+ **/
+const guint8 *
+gcm_ddc_device_get_edid (GcmDdcDevice *device, gsize *length, GError **error)
+{
+ gboolean ret;
+ const guint8 *data = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_edid (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ data = device->priv->edid_data;
+ if (length != NULL)
+ *length = device->priv->edid_length;
+out:
+ return data;
+}
+
+/**
+ * gcm_ddc_device_get_edid_md5:
+ **/
+const gchar *
+gcm_ddc_device_get_edid_md5 (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret;
+ const gchar *data = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_edid (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ data = device->priv->edid_md5;
+out:
+ return data;
+}
+
+/**
+ * gcm_ddc_device_get_pnpid:
+ **/
+const gchar *
+gcm_ddc_device_get_pnpid (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret;
+ const gchar *pnpid = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_edid (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ pnpid = device->priv->pnpid;
+out:
+ return pnpid;
+}
+
+/**
+ * gcm_ddc_device_get_model:
+ **/
+const gchar *
+gcm_ddc_device_get_model (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret;
+ const gchar *model = NULL;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_controls (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ model = device->priv->model;
+out:
+ return model;
+}
+
+/**
+ * gcm_ddc_device_get_kind:
+ **/
+GcmDdcDeviceKind
+gcm_ddc_device_get_kind (GcmDdcDevice *device, GError **error)
+{
+ gboolean ret;
+ GcmDdcDeviceKind kind = GCM_DDC_DEVICE_KIND_UNKNOWN;
+
+ g_return_val_if_fail (GCM_IS_DDC_DEVICE(device), kind);
+ g_return_val_if_fail (error == NULL || *error == NULL, kind);
+
+ /* get capabilities */
+ ret = gcm_ddc_device_ensure_controls (device, error);
+ if (!ret)
+ goto out;
+
+ /* success */
+ kind = device->priv->kind;
+out:
+ return kind;
+}
+
+/**
+ * gcm_ddc_device_set_verbose:
+ **/
+void
+gcm_ddc_device_set_verbose (GcmDdcDevice *device, GcmVerbose verbose)
+{
+ g_return_if_fail (GCM_IS_DDC_DEVICE(device));
+ device->priv->verbose = verbose;
+}
+
+/**
+ * gcm_ddc_device_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+gcm_ddc_device_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("gcm_ddc_device_error");
+ return quark;
+}
+
+/**
+ * gcm_ddc_device_get_property:
+ **/
+static void
+gcm_ddc_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GcmDdcDevice *device = GCM_DDC_DEVICE (object);
+ GcmDdcDevicePrivate *priv = device->priv;
+
+ switch (prop_id) {
+ case PROP_HAS_EDID:
+ g_value_set_boolean (value, priv->has_edid);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_device_set_property:
+ **/
+static void
+gcm_ddc_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gcm_ddc_device_class_init:
+ **/
+static void
+gcm_ddc_device_class_init (GcmDdcDeviceClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gcm_ddc_device_finalize;
+ object_class->get_property = gcm_ddc_device_get_property;
+ object_class->set_property = gcm_ddc_device_set_property;
+
+ /**
+ * GcmDdcDevice:has-coldplug:
+ *
+ * Since: 0.0.1
+ */
+ pspec = g_param_spec_boolean ("has-coldplug", NULL, "if there are no transactions in progress on this device",
+ TRUE,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_HAS_EDID, pspec);
+
+ g_type_class_add_private (klass, sizeof (GcmDdcDevicePrivate));
+}
+
+/**
+ * gcm_ddc_device_init:
+ **/
+static void
+gcm_ddc_device_init (GcmDdcDevice *device)
+{
+ device->priv = GCM_DDC_DEVICE_GET_PRIVATE (device);
+ device->priv->kind = GCM_DDC_DEVICE_KIND_UNKNOWN;
+ device->priv->addr = GCM_DEFAULT_DDCCI_ADDR;
+ device->priv->controls = g_ptr_array_new ();
+ device->priv->fd = -1;
+ /* assume the hardware is busy */
+ device->priv->required_wait = GCM_WRITE_DELAY_SECS;
+ device->priv->timer = g_timer_new ();
+}
+
+/**
+ * gcm_ddc_device_finalize:
+ **/
+static void
+gcm_ddc_device_finalize (GObject *object)
+{
+ GcmDdcDevice *device = GCM_DDC_DEVICE (object);
+ GcmDdcDevicePrivate *priv = device->priv;
+
+ g_return_if_fail (GCM_IS_DDC_DEVICE(device));
+ if (priv->fd > 0)
+ close (priv->fd);
+ g_free (priv->model);
+ g_free (priv->pnpid);
+ g_free (priv->edid_data);
+ g_free (priv->edid_md5);
+ g_timer_destroy (priv->timer);
+ g_ptr_array_free (priv->controls, TRUE);
+
+ G_OBJECT_CLASS (gcm_ddc_device_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_ddc_device_new:
+ *
+ * GcmDdcDevice is a nice GObject wrapper for gcm.
+ *
+ * Return value: A new %GcmDdcDevice instance
+ *
+ * Since: 0.0.1
+ **/
+GcmDdcDevice *
+gcm_ddc_device_new (void)
+{
+ GcmDdcDevice *device;
+ device = g_object_new (GCM_TYPE_DDC_DEVICE, NULL);
+ return GCM_DDC_DEVICE (device);
+}
diff --git a/libcolor-glib/gcm-ddc-device.h b/libcolor-glib/gcm-ddc-device.h
new file mode 100644
index 0000000..bc7b507
--- /dev/null
+++ b/libcolor-glib/gcm-ddc-device.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__GCM_H_INSIDE__) && !defined (GCM_COMPILATION)
+#error "Only <gcm.h> can be included directly."
+#endif
+
+#ifndef __GCM_DDC_DEVICE_H
+#define __GCM_DDC_DEVICE_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <gcm-ddc-common.h>
+#include <gcm-ddc-device.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_DDC_DEVICE (gcm_ddc_device_get_type ())
+#define GCM_DDC_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DDC_DEVICE, GcmDdcDevice))
+#define GCM_DDC_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DDC_DEVICE, GcmDdcDeviceClass))
+#define GCM_IS_DDC_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DDC_DEVICE))
+#define GCM_IS_DDC_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DDC_DEVICE))
+#define GCM_DDC_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DDC_DEVICE, GcmDdcDeviceClass))
+#define GCM_DDC_DEVICE_ERROR (gcm_ddc_device_error_quark ())
+#define GCM_DDC_DEVICE_TYPE_ERROR (gcm_ddc_device_error_get_type ())
+
+/**
+ * GcmDdcDeviceError:
+ * @GCM_DDC_DEVICE_ERROR_FAILED: the transaction failed for an unknown reason
+ *
+ * Errors that can be thrown
+ */
+typedef enum
+{
+ GCM_DDC_DEVICE_ERROR_FAILED
+} GcmDdcDeviceError;
+
+typedef struct _GcmDdcDevicePrivate GcmDdcDevicePrivate;
+typedef struct _GcmDdcDevice GcmDdcDevice;
+typedef struct _GcmDdcDeviceClass GcmDdcDeviceClass;
+
+struct _GcmDdcDevice
+{
+ GObject parent;
+ GcmDdcDevicePrivate *priv;
+};
+
+struct _GcmDdcDeviceClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+ void (* changed) (GcmDdcDevice *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);
+};
+
+typedef enum {
+ GCM_DDC_DEVICE_KIND_LCD,
+ GCM_DDC_DEVICE_KIND_CRT,
+ GCM_DDC_DEVICE_KIND_UNKNOWN
+} GcmDdcDeviceKind;
+
+/* incest */
+#include <gcm-ddc-control.h>
+
+GQuark gcm_ddc_device_error_quark (void);
+GType gcm_ddc_device_get_type (void);
+GcmDdcDevice *gcm_ddc_device_new (void);
+
+gboolean gcm_ddc_device_open (GcmDdcDevice *device,
+ const gchar *filename,
+ GError **error);
+gboolean gcm_ddc_device_close (GcmDdcDevice *device,
+ GError **error);
+const guint8 *gcm_ddc_device_get_edid (GcmDdcDevice *device,
+ gsize *length,
+ GError **error);
+const gchar *gcm_ddc_device_get_edid_md5 (GcmDdcDevice *device,
+ GError **error);
+gboolean gcm_ddc_device_write (GcmDdcDevice *device,
+ guchar *data,
+ gsize length,
+ GError **error);
+gboolean gcm_ddc_device_read (GcmDdcDevice *device,
+ guchar *data,
+ gsize data_length,
+ gsize *recieved_length,
+ GError **error);
+gboolean gcm_ddc_device_save (GcmDdcDevice *device,
+ GError **error);
+const gchar *gcm_ddc_device_get_pnpid (GcmDdcDevice *device,
+ GError **error);
+const gchar *gcm_ddc_device_get_model (GcmDdcDevice *device,
+ GError **error);
+GcmDdcDeviceKind gcm_ddc_device_get_kind (GcmDdcDevice *device,
+ GError **error);
+GPtrArray *gcm_ddc_device_get_controls (GcmDdcDevice *device,
+ GError **error);
+GcmDdcControl *gcm_ddc_device_get_control_by_id (GcmDdcDevice *device,
+ guchar id,
+ GError **error);
+void gcm_ddc_device_set_verbose (GcmDdcDevice *device,
+ GcmVerbose verbose);
+
+G_END_DECLS
+
+#endif /* __GCM_DDC_DEVICE_H */
+
diff --git a/libcolor-glib/gcm-self-test.c b/libcolor-glib/gcm-self-test.c
index db59fdc..1abab34 100644
--- a/libcolor-glib/gcm-self-test.c
+++ b/libcolor-glib/gcm-self-test.c
@@ -24,6 +24,8 @@
#include <glib-object.h>
#include "gcm-common.h"
+#include "gcm-ddc-client.h"
+#include "gcm-ddc-device.h"
static void
gcm_test_common_func (void)
@@ -109,6 +111,28 @@ gcm_test_common_func (void)
g_assert_cmpfloat (mat.m22, >, -0.001f);
}
+static void
+gcm_test_ddc_device_func (void)
+{
+ GcmDdcDevice *device;
+
+ device = gcm_ddc_device_new ();
+ g_assert (device != NULL);
+
+ g_object_unref (device);
+}
+
+static void
+gcm_test_ddc_client_func (void)
+{
+ GcmDdcClient *client;
+
+ client = gcm_ddc_client_new ();
+ g_assert (client != NULL);
+
+ g_object_unref (client);
+}
+
int
main (int argc, char **argv)
{
@@ -118,6 +142,8 @@ main (int argc, char **argv)
/* tests go here */
g_test_add_func ("/libcolor-glib/common", gcm_test_common_func);
+ g_test_add_func ("/libcolor-glib/ddc-device", gcm_test_ddc_device_func);
+ g_test_add_func ("/libcolor-glib/ddc-client", gcm_test_ddc_client_func);
return g_test_run ();
}
diff --git a/libcolor-glib/libcolor-glib.h b/libcolor-glib/libcolor-glib.h
index c26a71b..3a2d42d 100644
--- a/libcolor-glib/libcolor-glib.h
+++ b/libcolor-glib/libcolor-glib.h
@@ -29,6 +29,10 @@
#define __GCM_H_INSIDE__
#include <gcm-common.h>
+#include <gcm-ddc-common.h>
+#include <gcm-ddc-device.h>
+#include <gcm-ddc-client.h>
+#include <gcm-ddc-control.h>
#undef __GCM_H_INSIDE__
diff --git a/src/.gitignore b/src/.gitignore
index 133d6d8..389a65d 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -18,3 +18,4 @@ gcm-self-test
gcm-install-system-wide
gcm-helper-exiv
gcm-glsl-demo
+gcm-ddc-util
diff --git a/src/Makefile.am b/src/Makefile.am
index 720b2f1..d027be1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,6 +17,7 @@ INCLUDES = \
-DG_UDEV_API_IS_SUBJECT_TO_CHANGE \
-DGNOME_DESKTOP_USE_UNSTABLE_API \
$(GUDEV_CFLAGS) \
+ -I$(top_srcdir)/libcolor-glib \
-DBINDIR=\"$(bindir)\" \
-DLIBEXECDIR=\"$(libexecdir)\" \
-DSBINDIR=\"$(sbindir)\" \
@@ -26,6 +27,7 @@ INCLUDES = \
-DLOCALEDIR=\""$(localedir)"\" \
-DTESTDATADIR=\""$(top_srcdir)/data/tests"\" \
-DGCM_SYSTEM_PROFILES_DIR="\"$(GCM_SYSTEM_PROFILES_DIR)"\" \
+ -DI_KNOW_THE_GCM_GLIB_API_IS_SUBJECT_TO_CHANGE \
-DGCM_DATA=\"$(pkgdatadir)\"
introspectiondir = $(datadir)/dbus-1/interfaces
@@ -116,6 +118,7 @@ bin_PROGRAMS = \
gcm-viewer \
gcm-session \
gcm-picker \
+ gcm-ddc-util \
gcm-import
if HAVE_EXIV
@@ -340,6 +343,19 @@ libcolor_la_LIBADD = \
libcolor_la_LDFLAGS = -avoid-version -module
libcolor_la_CFLAGS = $(WARNINGFLAGS_C)
+COLOR_GLIB_LIBS = \
+ $(top_builddir)/libcolor-glib/libcolor-glib.la
+
+gcm_ddc_util_SOURCES = \
+ gcm-ddc-util.c
+
+gcm_ddc_util_LDADD = \
+ $(GLIB_LIBS) \
+ $(COLOR_GLIB_LIBS)
+
+gcm_ddc_util_CFLAGS = \
+ $(WARNINGFLAGS_C)
+
if HAVE_TESTS
check_PROGRAMS = \
diff --git a/src/gcm-ddc-util.c b/src/gcm-ddc-util.c
new file mode 100644
index 0000000..0aa80d1
--- /dev/null
+++ b/src/gcm-ddc-util.c
@@ -0,0 +1,310 @@
+/* -*- 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.
+ */
+
+#include <config.h>
+#include <glib/gstdio.h>
+
+#include <libcolor-glib.h>
+
+/**
+ * show_device_md5_cb:
+ **/
+static void
+show_device_md5_cb (GcmDdcDevice *device, gpointer user_data)
+{
+ GError *error = NULL;
+ const gchar *desc;
+
+ desc = gcm_ddc_device_get_edid_md5 (device, &error);
+ if (desc == NULL) {
+ g_warning ("failed to get EDID: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ g_print ("EDID: \t%s\n", desc);
+out:
+ return;
+}
+
+/**
+ * show_device:
+ **/
+static void
+show_device (GcmDdcDevice *device)
+{
+ guint i, j;
+ GPtrArray *array;
+ GcmDdcControl *control;
+ GArray *values;
+ GError *error = NULL;
+ const gchar *desc;
+
+ desc = gcm_ddc_device_get_edid_md5 (device, &error);
+ if (desc == NULL) {
+ g_warning ("failed to get EDID: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ g_print ("EDID: \t%s\n", desc);
+
+ desc = gcm_ddc_device_get_pnpid (device, &error);
+ if (desc == NULL) {
+ g_warning ("failed to get PNPID: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ g_print ("PNPID:\t%s\n", desc);
+
+ desc = gcm_ddc_device_get_model (device, &error);
+ if (desc == NULL) {
+ g_warning ("failed to get model: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ g_print ("Model:\t%s\n", desc);
+
+ array = gcm_ddc_device_get_controls (device, &error);
+ if (array == NULL) {
+ g_warning ("failed to get caps: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ for (i = 0; i < array->len; i++) {
+ control = g_ptr_array_index (array, i);
+
+ desc = gcm_ddc_control_get_description (control);
+ g_print ("0x%02x\t[%s]", gcm_ddc_control_get_id (control), desc != NULL ? desc : "unknown");
+
+ /* print allowed values */
+ values = gcm_ddc_control_get_values (control);
+ if (values->len > 0) {
+ g_print ("\t ( ");
+ for (j=0; j<values->len; j++) {
+ g_print ("%i ", g_array_index (values, guint16, j));
+ }
+ g_print (")");
+ }
+ g_print ("\n");
+ g_array_unref (values);
+ }
+ g_ptr_array_unref (array);
+out:
+ return;
+}
+
+/**
+ * show_device_cb:
+ **/
+static void
+show_device_cb (GcmDdcDevice *device, gpointer user_data)
+{
+ show_device (device);
+}
+
+/**
+ * main:
+ **/
+int
+main (int argc, char **argv)
+{
+ gboolean ret;
+ GcmVerbose verbose = GCM_VERBOSE_NONE;
+ gboolean enumerate = FALSE;
+ gboolean caps = FALSE;
+ gchar *display_md5 = NULL;
+ gchar *control_name = NULL;
+ gboolean control_get = FALSE;
+ gint control_set = -1;
+ GcmDdcClient *client;
+ GcmDdcDevice *device = NULL;
+ GcmDdcControl *control = NULL;
+ gint brightness = -1;
+ GOptionContext *context;
+ GError *error = NULL;
+ GPtrArray *array;
+ guint16 value, max;
+ guchar idx;
+ guint i;
+
+ const GOptionEntry options[] = {
+ { "verbose", '\0', 0, G_OPTION_ARG_INT, &verbose,
+ "Enable verbose debugging mode", NULL},
+ { "enumerate", '\0', 0, G_OPTION_ARG_NONE, &enumerate,
+ "Enumerate all displays and display capabilities", NULL},
+ { "display", '\0', 0, G_OPTION_ARG_STRING, &display_md5,
+ "Set the display MD5 to operate on", NULL},
+ { "caps", '\0', 0, G_OPTION_ARG_NONE, &caps,
+ "Print the capabilities of the selected display", NULL},
+ { "brightness", '\0', 0, G_OPTION_ARG_INT, &brightness,
+ "Set the display brightness", NULL},
+ { "control", '\0', 0, G_OPTION_ARG_STRING, &control_name,
+ "Use a control value, e.g. 'select-color-preset'", NULL},
+ { "get", '\0', 0, G_OPTION_ARG_NONE, &control_get,
+ "Get a control value", NULL},
+ { "set", '\0', 0, G_OPTION_ARG_INT, &control_set,
+ "Set a control value", NULL},
+ { NULL}
+ };
+
+ g_type_init ();
+
+ context = g_option_context_new ("DDC/CI utility program");
+ g_option_context_set_summary (context, "This exposes the DDC/CI protocol used by most modern displays.");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ client = gcm_ddc_client_new ();
+ gcm_ddc_client_set_verbose (client, verbose);
+
+ /* we want to enumerate all devices */
+ if (enumerate) {
+ array = gcm_ddc_client_get_devices (client, &error);
+ if (array == NULL) {
+ g_warning ("failed to get device list: %s", error->message);
+ goto out;
+ }
+
+ /* show device details */
+ g_ptr_array_foreach (array, (GFunc) show_device_cb, NULL);
+ g_ptr_array_unref (array);
+ goto out;
+ }
+
+ if (display_md5 == NULL) {
+ array = gcm_ddc_client_get_devices (client, &error);
+ if (array == NULL) {
+ g_warning ("failed to get device list: %s", error->message);
+ goto out;
+ }
+
+ /* show device details */
+ g_print ("No --display specified, please select from:\n");
+ g_ptr_array_foreach (array, (GFunc) show_device_md5_cb, NULL);
+ g_ptr_array_unref (array);
+ goto out;
+ }
+
+ /* get the correct device */
+ device = gcm_ddc_client_get_device_from_edid (client, display_md5, &error);
+ if (device == NULL) {
+ g_warning ("failed to get device list: %s", error->message);
+ goto out;
+ }
+
+ /* get caps? */
+ if (caps) {
+ show_device (device);
+ goto out;
+ }
+
+ /* set brightness? */
+ if (brightness != -1) {
+ control = gcm_ddc_device_get_control_by_id (device, GCM_DDC_CONTROL_ID_BRIGHTNESS, &error);
+ if (control == NULL) {
+ g_warning ("Failed to get brightness control: %s", error->message);
+ goto out;
+ }
+
+ /* get old value */
+ ret = gcm_ddc_control_request (control, &value, &max, &error);
+ if (!ret) {
+ g_warning ("failed to read: %s", error->message);
+ goto out;
+ }
+
+ /* set new value */
+ ret = gcm_ddc_control_set (control, brightness, &error);
+ if (!ret) {
+ g_warning ("failed to write: %s", error->message);
+ goto out;
+ }
+
+ /* print what we did */
+ g_print ("brightness before was %i%%, now is %i%% max is %i%%\n", value, brightness, max);
+ g_object_unref (control);
+ goto out;
+ }
+
+ /* get named control */
+ if (control_name == NULL) {
+ g_print ("you need to specify a control name with --control\n");
+ show_device (device);
+ }
+ idx = gcm_get_vcp_index_from_description (control_name);
+ if (idx == GCM_VCP_ID_INVALID) {
+ const gchar *description;
+ g_warning ("Failed to match description, choose from:");
+ for (i=0; i<0xff; i++) {
+ description = gcm_get_vcp_description_from_index (i);
+ if (description != NULL)
+ g_print ("* %s\n", description);
+ }
+ goto out;
+ }
+
+ /* get control */
+ control = gcm_ddc_device_get_control_by_id (device, idx, &error);
+ if (control == NULL) {
+ g_warning ("Failed to get control: %s", error->message);
+ goto out;
+ }
+
+ /* get named control */
+ if (control_get) {
+ /* get old value */
+ ret = gcm_ddc_control_request (control, &value, &max, &error);
+ if (!ret) {
+ g_warning ("failed to read: %s", error->message);
+ goto out;
+ }
+
+ /* print what we got */
+ g_print ("%s is %i, max is %i\n", control_name, value, max);
+ }
+
+ /* set named control */
+ if (control_set != -1) {
+
+ /* set new value */
+ ret = gcm_ddc_control_set (control, control_set, &error);
+ if (!ret) {
+ g_warning ("failed to write: %s", error->message);
+ goto out;
+ }
+ }
+out:
+ g_clear_error (&error);
+ ret = gcm_ddc_client_close (client, &error);
+ if (!ret) {
+ g_warning ("failed to close: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+ if (device != NULL)
+ g_object_unref (device);
+ if (control != NULL)
+ g_object_unref (control);
+ g_object_unref (client);
+ g_free (display_md5);
+ g_free (control_name);
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]