Blob Blame History Raw
diff -up gnome-desktop-2.22.2/configure.in.add-randr-12 gnome-desktop-2.22.2/configure.in
--- gnome-desktop-2.22.2/configure.in.add-randr-12	2008-05-26 12:25:40.000000000 -0400
+++ gnome-desktop-2.22.2/configure.in	2008-05-28 09:28:15.000000000 -0400
@@ -51,10 +51,10 @@ AC_SUBST(GNOME_MICRO)
 AC_SUBST(GNOME_DISTRIBUTOR)
 AC_SUBST(GNOME_DATE)
 
-GNOME_COMMON_INIT
-GNOME_DEBUG_CHECK
-GNOME_COMPILE_WARNINGS([maximum])
-GNOME_MAINTAINER_MODE_DEFINES
+#GNOME_COMMON_INIT
+#GNOME_DEBUG_CHECK
+#GNOME_COMPILE_WARNINGS([maximum])
+#GNOME_MAINTAINER_MODE_DEFINES
 
 # As a special favour for vuntz, support --disable-deprecations
 
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/display-name.c
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/display-name.c	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Author: Soren Sandmann <sandmann@redhat.com> */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include "edid.h"
+
+typedef struct Vendor Vendor;
+struct Vendor
+{
+    const char vendor_id[4];
+    const char vendor_name[28];
+};
+
+/* This list of vendor codes derived from lshw
+ * 
+ * http://ezix.org/project/wiki/HardwareLiSter
+ */
+static const struct Vendor vendors[] = 
+{
+    { "AIC", "AG Neovo" },
+    { "ACR", "Acer" },
+    { "DEL", "DELL" },
+    { "SAM", "SAMSUNG" },
+    { "SNY", "SONY" },
+    { "SEC", "Epson" },
+    { "WAC", "Wacom" },
+    { "NEC", "NEC" },
+    { "CMO", "CMO" },	/* Chi Mei */
+    { "BNQ", "BenQ" },
+
+    { "ABP", "Advansys" },
+    { "ACC", "Accton" },
+    { "ACE", "Accton" },
+    { "ADP", "Adaptec" },
+    { "ADV", "AMD" },
+    { "AIR", "AIR" },
+    { "AMI", "AMI" },
+    { "ASU", "ASUS" },
+    { "ATI", "ATI" },
+    { "ATK", "Allied Telesyn" },
+    { "AZT", "Aztech" },
+    { "BAN", "Banya" },
+    { "BRI", "Boca Research" },
+    { "BUS", "Buslogic" },
+    { "CCI", "Cache Computers Inc." },
+    { "CHA", "Chase" },
+    { "CMD", "CMD Technology, Inc." },
+    { "COG", "Cogent" },
+    { "CPQ", "Compaq" },
+    { "CRS", "Crescendo" },
+    { "CSC", "Crystal" },
+    { "CSI", "CSI" },
+    { "CTL", "Creative Labs" },
+    { "DBI", "Digi" },
+    { "DEC", "Digital Equipment" },
+    { "DBK", "Databook" },
+    { "EGL", "Eagle Technology" },
+    { "ELS", "ELSA" },
+    { "ESS", "ESS" },
+    { "FAR", "Farallon" },
+    { "FDC", "Future Domain" },
+    { "HWP", "Hewlett-Packard" },
+    { "IBM", "IBM" },
+    { "INT", "Intel" },
+    { "ISA", "Iomega" },
+    { "MDG", "Madge" },
+    { "MDY", "Microdyne" },
+    { "MET", "Metheus" },
+    { "MIC", "Micronics" },
+    { "MLX", "Mylex" },
+    { "NVL", "Novell" },
+    { "OLC", "Olicom" },
+    { "PRO", "Proteon" },
+    { "RII", "Racal" },
+    { "RTL", "Realtek" },
+    { "SCM", "SCM" },
+    { "SKD", "SysKonnect" },
+    { "SGI", "SGI" },
+    { "SMC", "SMC" },
+    { "SNI", "Siemens Nixdorf" },
+    { "STL", "Stallion Technologies" },
+    { "SUN", "Sun" },
+    { "SUP", "SupraExpress" },
+    { "SVE", "SVEC" },
+    { "TCC", "Thomas-Conrad" },
+    { "TCI", "Tulip" },
+    { "TCM", "3Com" },
+    { "TCO", "Thomas-Conrad" },
+    { "TEC", "Tecmar" },
+    { "TRU", "Truevision" },
+    { "TOS", "Toshiba" },
+    { "TYN", "Tyan" },
+    { "UBI", "Ungermann-Bass" },
+    { "USC", "UltraStor" },
+    { "VDM", "Vadem" },
+    { "VMI", "Vermont" },
+    { "WDC", "Western Digital" },
+    { "ZDS", "Zeos" },
+
+    /* From http://faydoc.tripod.com/structures/01/0136.htm */
+    { "ACT", "Targa" },
+    { "ADI", "ADI" },
+    { "AOC", "AOC Intl" },
+    { "API", "Acer America" },
+    { "APP", "Apple Computer" },
+    { "ART", "ArtMedia" },
+    { "AST", "AST Research" },
+    { "CPL", "Compal" },
+    { "CTX", "Chuntex Electronic Co." },
+    { "DPC", "Delta Electronics" },
+    { "DWE", "Daewoo" },
+    { "ECS", "ELITEGROUP" },
+    { "EIZ", "EIZO" },
+    { "FCM", "Funai" },
+    { "GSM", "LG Electronics" },
+    { "GWY", "Gateway 2000" },
+    { "HEI", "Hyundai" },
+    { "HIT", "Hitachi" },
+    { "HSL", "Hansol" },
+    { "HTC", "Hitachi" },
+    { "ICL", "Fujitsu ICL" },
+    { "IVM", "Idek Iiyama" },
+    { "KFC", "KFC Computek" },
+    { "LKM", "ADLAS" },
+    { "LNK", "LINK Tech" },
+    { "LTN", "Lite-On" },
+    { "MAG", "MAG InnoVision" },
+    { "MAX", "Maxdata" },
+    { "MEI", "Panasonic" },
+    { "MEL", "Mitsubishi" },
+    { "MIR", "miro" },
+    { "MTC", "MITAC" },
+    { "NAN", "NANAO" },
+    { "NEC", "NEC Tech" },
+    { "NOK", "Nokia" },
+    { "OQI", "OPTIQUEST" },
+    { "PBN", "Packard Bell" },
+    { "PGS", "Princeton" },
+    { "PHL", "Philips" },
+    { "REL", "Relisys" },
+    { "SDI", "Samtron" },
+    { "SMI", "Smile" },
+    { "SPT", "Sceptre" },
+    { "SRC", "Shamrock Technology" },
+    { "STP", "Sceptre" },
+    { "TAT", "Tatung" },
+    { "TRL", "Royal Information Company" },
+    { "TSB", "Toshiba, Inc." },
+    { "UNM", "Unisys" },
+    { "VSC", "ViewSonic" },
+    { "WTC", "Wen Tech" },
+    { "ZCM", "Zenith Data Systems" },
+
+    { "???", "Unknown" },
+};
+
+static const char *
+find_vendor (const char *code)
+{
+    int i;
+
+    for (i = 0; i < sizeof (vendors) / sizeof (vendors[0]); ++i)
+    {
+	const Vendor *v = &(vendors[i]);
+	
+	if (strcmp (v->vendor_id, code) == 0)
+	    return v->vendor_name;
+    }
+
+    return code;
+};
+
+char *
+make_display_name (const char *output_name,
+		   const MonitorInfo *info)
+{
+    const char *vendor;
+    int width_mm, height_mm, inches;
+
+    if (output_name &&
+	(strstr ("lvds", output_name)	||
+	 strstr ("LVDS", output_name)	||
+	 strstr ("Lvds", output_name)))
+    {
+	vendor = "Laptop";
+    }
+    else if (info)
+    {
+	vendor = find_vendor (info->manufacturer_code);
+    }
+    else
+    {
+	vendor = "Unknown";
+    }
+
+    if (info && info->width_mm != -1 && info->height_mm)
+    {
+	width_mm = info->width_mm;
+	height_mm = info->height_mm;
+    }
+    else if (info && info->n_detailed_timings)
+    {
+	width_mm = info->detailed_timings[0].width_mm;
+	height_mm = info->detailed_timings[0].height_mm;
+    }
+    else
+    {
+	width_mm = -1;
+	height_mm = -1;
+    }
+    
+    if (width_mm != -1 && height_mm != -1)
+    {
+	double d = sqrt (width_mm * width_mm + height_mm * height_mm);
+
+	inches = (int)(d / 25.4 + 0.5);
+    }
+    else
+    {
+	inches = -1;
+    }
+
+    if (inches > 0)
+	return g_strdup_printf ("%s %d\"", vendor, inches);
+    else
+	return g_strdup_printf ("%s\n", vendor);
+}
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/edid.h
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/edid.h	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,170 @@
+typedef unsigned char uchar;
+typedef struct MonitorInfo MonitorInfo;
+typedef struct Timing Timing;
+typedef struct DetailedTiming DetailedTiming;
+
+typedef enum
+{
+    UNDEFINED,
+    DVI,
+    HDMI_A,
+    HDMI_B,
+    MDDI,
+    DISPLAY_PORT
+} Interface;
+
+typedef enum
+{
+    UNDEFINED_COLOR,
+    MONOCHROME,
+    RGB,
+    OTHER_COLOR
+} ColorType;
+
+typedef enum
+{
+    NO_STEREO,
+    FIELD_RIGHT,
+    FIELD_LEFT,
+    TWO_WAY_RIGHT_ON_EVEN,
+    TWO_WAY_LEFT_ON_EVEN,
+    FOUR_WAY_INTERLEAVED,
+    SIDE_BY_SIDE
+} StereoType;
+
+struct Timing
+{
+    int width;
+    int height;
+    int frequency;
+};
+
+struct DisplayDescriptor
+{
+};
+
+struct DetailedTiming
+{
+    int		pixel_clock;
+    int		h_addr;
+    int		h_blank;
+    int		h_sync;
+    int		h_front_porch;
+    int		v_addr;
+    int		v_blank;
+    int		v_sync;
+    int		v_front_porch;
+    int		width_mm;
+    int		height_mm;
+    int		right_border;
+    int		top_border;
+    int		interlaced;
+    StereoType	stereo;
+
+    int		digital_sync;
+    union
+    {
+	struct
+	{
+	    int bipolar;
+	    int serrations;
+	    int sync_on_green;
+	} analog;
+
+	struct
+	{
+	    int composite;
+	    int serrations;
+	    int negative_vsync;
+	    int negative_hsync;
+	} digital;
+    };
+};
+
+struct MonitorInfo
+{
+    int			checksum;
+    char		manufacturer_code[4];
+    int			product_code;
+    unsigned int	serial_number;
+    
+    int			production_week;	/* -1 if not specified */
+    int			production_year;	/* -1 if not specified */
+    int			model_year;		/* -1 if not specified */
+
+    int			major_version;
+    int			minor_version;
+
+    int			is_digital;
+    
+    union
+    {
+	struct
+	{
+	    int		bits_per_primary;
+	    Interface	interface;
+	    int		rgb444;
+	    int		ycrcb444;
+	    int		ycrcb422;
+	} digital;
+
+	struct
+	{
+	    double	video_signal_level;
+	    double	sync_signal_level;
+	    double	total_signal_level;
+
+	    int		blank_to_black;
+
+	    int		separate_hv_sync;
+	    int		composite_sync_on_h;
+	    int		composite_sync_on_green;
+	    int		serration_on_vsync;
+	    ColorType	color_type;
+	} analog;
+    };
+
+    int			width_mm;		/* -1 if not specified */
+    int			height_mm;		/* -1 if not specified */
+    double		aspect_ratio;		/* -1.0 if not specififed */
+
+    double		gamma;			/* -1.0 if not specified */
+
+    int			standby;
+    int			suspend;
+    int			active_off;
+
+    int			srgb_is_standard;
+    int			preferred_timing_includes_native;
+    int			continuous_frequency;
+
+    double		red_x;
+    double		red_y;
+    double		green_x;
+    double		green_y;
+    double		blue_x;
+    double		blue_y;
+    double		white_x;
+    double		white_y;
+
+    Timing		established[24];	/* Terminated by 0x0x0 */
+    Timing		standard[8];
+    
+    int			n_detailed_timings;
+    DetailedTiming	detailed_timings[4];	/* If monitor has a preferred
+						 * mode, it is the first one
+						 * (whether it has, is
+						 * determined by the 
+						 * preferred_timing_includes
+						 * bit.
+						 */
+
+    /* Optional product description */
+    char		dsc_serial_number[14];
+    char		dsc_product_name[14];
+    char		dsc_string[14];		/* Unspecified ASCII data */
+};
+
+MonitorInfo *decode_edid (const uchar *data);
+char *       make_display_name (const char        *output_name,
+				const MonitorInfo *info);
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/edid-parse.c
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/edid-parse.c	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Author: Soren Sandmann <sandmann@redhat.com> */
+
+#include "edid.h"
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define TRUE 1
+#define FALSE 0
+
+static int
+get_bit (int in, int bit)
+{
+    return (in & (1 << bit)) >> bit;
+}
+
+static int
+get_bits (int in, int begin, int end)
+{
+    int mask = (1 << (end - begin + 1)) - 1;
+    
+    return (in >> begin) & mask;
+}
+
+static int
+decode_header (const uchar *edid)
+{
+    if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
+	return TRUE;
+    return FALSE;
+}
+
+static int
+decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
+{
+    int is_model_year;
+    
+    /* Manufacturer Code */
+    info->manufacturer_code[0]  = get_bits (edid[0x08], 2, 6);
+    info->manufacturer_code[1]  = get_bits (edid[0x08], 0, 1) << 3;
+    info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
+    info->manufacturer_code[2]  = get_bits (edid[0x09], 0, 4);
+    info->manufacturer_code[3]  = '\0';
+    
+    info->manufacturer_code[0] += 'A' - 1;
+    info->manufacturer_code[1] += 'A' - 1;
+    info->manufacturer_code[2] += 'A' - 1;
+
+    /* Product Code */
+    info->product_code = edid[0x0b] << 8 | edid[0x0a];
+
+    /* Serial Number */
+    info->serial_number =
+	edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
+
+    /* Week and Year */
+    is_model_year = FALSE;
+    switch (edid[0x10])
+    {
+    case 0x00:
+	info->production_week = -1;
+	break;
+
+    case 0xff:
+	info->production_week = -1;
+	is_model_year = TRUE;
+	break;
+
+    default:
+	info->production_week = edid[0x10];
+	break;
+    }
+
+    if (is_model_year)
+    {
+	info->production_year = -1;
+	info->model_year = 1990 + edid[0x11];
+    }
+    else
+    {
+	info->production_year = 1990 + edid[0x11];
+	info->model_year = -1;
+    }
+
+    return TRUE;
+}
+
+static int
+decode_edid_version (const uchar *edid, MonitorInfo *info)
+{
+    info->major_version = edid[0x12];
+    info->minor_version = edid[0x13];
+
+    return TRUE;
+}
+
+static int
+decode_display_parameters (const uchar *edid, MonitorInfo *info)
+{
+    /* Digital vs Analog */
+    info->is_digital = get_bit (edid[0x14], 7);
+
+    if (info->is_digital)
+    {
+	int bits;
+	
+	static const int bit_depth[8] =
+	{
+	    -1, 6, 8, 10, 12, 14, 16, -1
+	};
+
+	static const Interface interfaces[6] =
+	{
+	    UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
+	};
+
+	bits = get_bits (edid[0x14], 4, 6);
+	info->digital.bits_per_primary = bit_depth[bits];
+
+	bits = get_bits (edid[0x14], 0, 3);
+	
+	if (bits <= 5)
+	    info->digital.interface = interfaces[bits];
+	else
+	    info->digital.interface = UNDEFINED;
+    }
+    else
+    {
+	int bits = get_bits (edid[0x14], 5, 6);
+	
+	static const double levels[][3] =
+	{
+	    { 0.7,   0.3,    1.0 },
+	    { 0.714, 0.286,  1.0 },
+	    { 1.0,   0.4,    1.4 },
+	    { 0.7,   0.0,    0.7 },
+	};
+
+	info->analog.video_signal_level = levels[bits][0];
+	info->analog.sync_signal_level = levels[bits][1];
+	info->analog.total_signal_level = levels[bits][2];
+
+	info->analog.blank_to_black = get_bit (edid[0x14], 4);
+
+	info->analog.separate_hv_sync = get_bit (edid[0x14], 3);
+	info->analog.composite_sync_on_h = get_bit (edid[0x14], 2);
+	info->analog.composite_sync_on_green = get_bit (edid[0x14], 1);
+
+	info->analog.serration_on_vsync = get_bit (edid[0x14], 0);
+    }
+
+    /* Screen Size / Aspect Ratio */
+    if (edid[0x15] == 0 && edid[0x16] == 0)
+    {
+	info->width_mm = -1;
+	info->height_mm = -1;
+	info->aspect_ratio = -1.0;
+    }
+    else if (edid[0x16] == 0)
+    {
+	info->width_mm = -1;
+	info->height_mm = -1; 
+	info->aspect_ratio = 100.0 / (edid[0x15] + 99);
+    }
+    else if (edid[0x15] == 0)
+    {
+	info->width_mm = -1;
+	info->height_mm = -1;
+	info->aspect_ratio = 100.0 / (edid[0x16] + 99);
+	info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
+    }
+    else
+    {
+	info->width_mm = 10 * edid[0x15];
+	info->height_mm = 10 * edid[0x16];
+    }
+
+    /* Gamma */
+    if (edid[0x17] == 0xFF)
+	info->gamma = -1.0;
+    else
+	info->gamma = (edid[0x17] + 100.0) / 100.0;
+
+    /* Features */
+    info->standby = get_bit (edid[0x18], 7);
+    info->suspend = get_bit (edid[0x18], 6);
+    info->active_off = get_bit (edid[0x18], 5);
+
+    if (info->is_digital)
+    {
+	info->digital.rgb444 = TRUE;
+	if (get_bit (edid[0x18], 3))
+	    info->digital.ycrcb444 = 1;
+	if (get_bit (edid[0x18], 4))
+	    info->digital.ycrcb422 = 1;
+    }
+    else
+    {
+	int bits = get_bits (edid[0x18], 3, 4);
+	ColorType color_type[4] =
+	{
+	    MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
+	};
+
+	info->analog.color_type = color_type[bits];
+    }
+
+    info->srgb_is_standard = get_bit (edid[0x18], 2);
+
+    /* In 1.3 this is called "has preferred timing" */
+    info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
+
+    /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
+    info->continuous_frequency = get_bit (edid[0x18], 0);
+    return TRUE;
+}
+
+static double
+decode_fraction (int high, int low)
+{
+    double result = 0.0;
+    int i;
+
+    high = (high << 2) | low;
+
+    for (i = 0; i < 10; ++i)
+	result += get_bit (high, i) * pow (2, i - 10);
+
+    return result;
+}
+
+static int
+decode_color_characteristics (const uchar *edid, MonitorInfo *info)
+{
+    info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
+    info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
+    info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
+    info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
+    info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
+    info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
+    info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
+    info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
+
+    return TRUE;
+}
+
+static int
+decode_established_timings (const uchar *edid, MonitorInfo *info)
+{
+    static const Timing established[][8] = 
+    {
+	{
+	    { 800, 600, 60 },
+	    { 800, 600, 56 },
+	    { 640, 480, 75 },
+	    { 640, 480, 72 },
+	    { 640, 480, 67 },
+	    { 640, 480, 60 },
+	    { 720, 400, 88 },
+	    { 720, 400, 70 }
+	},
+	{
+	    { 1280, 1024, 75 },
+	    { 1024, 768, 75 },
+	    { 1024, 768, 70 },
+	    { 1024, 768, 60 },
+	    { 1024, 768, 87 },
+	    { 832, 624, 75 },
+	    { 800, 600, 75 },
+	    { 800, 600, 72 }
+	},
+	{
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 0, 0, 0 },
+	    { 1152, 870, 75 }
+	},
+    };
+
+    int i, j, idx;
+
+    idx = 0;
+    for (i = 0; i < 3; ++i)
+    {
+	for (j = 0; j < 8; ++j)
+	{
+	    int byte = edid[0x23 + i];
+
+	    if (get_bit (byte, j) && established[i][j].frequency != 0)
+		info->established[idx++] = established[i][j];
+	}
+    }
+    return TRUE;
+}
+
+static int
+decode_standard_timings (const uchar *edid, MonitorInfo *info)
+{
+    int i;
+    
+    for (i = 0; i < 8; i++)
+    {
+	int first = edid[0x26 + 2 * i];
+	int second = edid[0x27 + 2 * i];
+
+	if (first != 0x01 && second != 0x01)
+	{
+	    int w = 8 * (first + 31);
+	    int h;
+
+	    switch (get_bits (second, 6, 7))
+	    {
+	    case 0x00: h = (w / 16) * 10; break;
+	    case 0x01: h = (w / 4) * 3; break;
+	    case 0x02: h = (w / 5) * 4; break;
+	    case 0x03: h = (w / 16) * 9; break;
+	    }
+
+	    info->standard[i].width = w;
+	    info->standard[i].height = h;
+	    info->standard[i].frequency = get_bits (second, 0, 5) + 60;
+	}
+    }
+    
+    return TRUE;
+}
+
+static void
+decode_lf_string (const uchar *s, int n_chars, char *result)
+{
+    int i;
+    for (i = 0; i < n_chars; ++i)
+    {
+	if (s[i] == 0x0a)
+	{
+	    *result++ = '\0';
+	    break;
+	}
+	else if (s[i] == 0x00)
+	{
+	    /* Convert embedded 0's to spaces */
+	    *result++ = ' ';
+	}
+	else
+	{
+	    *result++ = s[i];
+	}
+    }
+}
+
+static void
+decode_display_descriptor (const uchar *desc,
+			   MonitorInfo *info)
+{
+    switch (desc[0x03])
+    {
+    case 0xFC:
+	decode_lf_string (desc + 5, 13, info->dsc_product_name);
+	break;
+    case 0xFF:
+	decode_lf_string (desc + 5, 13, info->dsc_serial_number);
+	break;
+    case 0xFE:
+	decode_lf_string (desc + 5, 13, info->dsc_string);
+	break;
+    case 0xFD:
+	/* Range Limits */
+	break;
+    case 0xFB:
+	/* Color Point */
+	break;
+    case 0xFA:
+	/* Timing Identifications */
+	break;
+    case 0xF9:
+	/* Color Management */
+	break;
+    case 0xF8:
+	/* Timing Codes */
+	break;
+    case 0xF7:
+	/* Established Timings */
+	break;
+    case 0x10:
+	break;
+    }
+}
+
+static void
+decode_detailed_timing (const uchar *timing,
+			DetailedTiming *detailed)
+{
+    int bits;
+    StereoType stereo[] =
+    {
+	NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
+	TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
+	FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
+    };
+    
+    detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
+    detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
+    detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
+    detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
+    detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
+    detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
+    detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
+    detailed->v_front_porch =
+	get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
+    detailed->v_sync =
+	get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
+    detailed->width_mm =  timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
+    detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
+    detailed->right_border = timing[0x0f];
+    detailed->top_border = timing[0x10];
+
+    detailed->interlaced = get_bit (timing[0x11], 7);
+
+    /* Stereo */
+    bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
+    detailed->stereo = stereo[bits];
+
+    /* Sync */
+    bits = timing[0x11];
+
+    detailed->digital_sync = get_bit (bits, 4);
+    if (detailed->digital_sync)
+    {
+	detailed->digital.composite = !get_bit (bits, 3);
+
+	if (detailed->digital.composite)
+	{
+	    detailed->digital.serrations = get_bit (bits, 2);
+	    detailed->digital.negative_vsync = FALSE;
+	}
+	else
+	{
+	    detailed->digital.serrations = FALSE;
+	    detailed->digital.negative_vsync = !get_bit (bits, 2);
+	}
+
+	detailed->digital.negative_hsync = !get_bit (bits, 0);
+    }
+    else
+    {
+	detailed->analog.bipolar = get_bit (bits, 3);
+	detailed->analog.serrations = get_bit (bits, 2);
+	detailed->analog.sync_on_green = !get_bit (bits, 1);
+    }
+}
+
+static int
+decode_descriptors (const uchar *edid, MonitorInfo *info)
+{
+    int i;
+    int timing_idx;
+    
+    timing_idx = 0;
+    
+    for (i = 0; i < 4; ++i)
+    {
+	int index = 0x36 + i * 18;
+
+	if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
+	{
+	    decode_display_descriptor (edid + index, info);
+	}
+	else
+	{
+	    decode_detailed_timing (
+		edid + index, &(info->detailed_timings[timing_idx++]));
+	}
+    }
+
+    info->n_detailed_timings = timing_idx;
+
+    return TRUE;
+}
+
+static void
+decode_check_sum (const uchar *edid,
+		  MonitorInfo *info)
+{
+    int i;
+    uchar check = 0;
+
+    for (i = 0; i < 128; ++i)
+	check += edid[i];
+
+    info->checksum = check;
+}
+
+MonitorInfo *
+decode_edid (const uchar *edid)
+{
+    MonitorInfo *info = calloc (1, sizeof (MonitorInfo));
+
+    decode_check_sum (edid, info);
+    
+    if (!decode_header (edid))
+	return NULL;
+
+    if (!decode_vendor_and_product_identification (edid, info))
+	return NULL;
+
+    if (!decode_edid_version (edid, info))
+	return NULL;
+
+    if (!decode_display_parameters (edid, info))
+	return NULL;
+
+    if (!decode_color_characteristics (edid, info))
+	return NULL;
+
+    if (!decode_established_timings (edid, info))
+	return NULL;
+
+    if (!decode_standard_timings (edid, info))
+	return NULL;
+    
+    if (!decode_descriptors (edid, info))
+	return NULL;
+    
+    return info;
+}
diff -up gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/Makefile.am.add-randr-12 gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/Makefile.am
--- gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/Makefile.am.add-randr-12	2008-05-26 12:10:56.000000000 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/Makefile.am	2008-05-28 09:28:15.000000000 -0400
@@ -1,5 +1,7 @@
 libgnomeui_desktopdir = $(includedir)/gnome-desktop-2.0/libgnomeui
-libgnomeui_desktop_HEADERS = \
-	gnome-ditem-edit.h \
-	gnome-hint.h       \
-	gnome-bg.h
+libgnomeui_desktop_HEADERS =	\
+	gnome-ditem-edit.h	\
+	gnome-hint.h		\
+	gnome-bg.h		\
+ 	randrwrap.h		\
+ 	monitor-db.h
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/monitor-db.h
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/monitor-db.h	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,52 @@
+#ifndef MONITOR_DB_H
+#define MONITOR_DB_H
+
+#include <libgnomeui/randrwrap.h>
+#include <glib.h>
+
+typedef struct Output Output;
+typedef struct Configuration Configuration;
+
+struct Output
+{
+    char *	name;
+
+    gboolean	on;
+    int		width;
+    int		height;
+    int		rate;
+    int		x;
+    int		y;
+    RWRotation	rotation;
+
+    gboolean	connected;
+    char	vendor[4];
+    guint	product;
+    guint	serial;
+    double	aspect;
+    int		pref_width;
+    int		pref_height;
+    char *      display_name;
+
+    gpointer	user_data;
+};
+
+struct Configuration
+{
+    gboolean clone;
+    
+    Output **outputs;
+};
+
+void            configuration_free         (Configuration  *configuration);
+Configuration  *configuration_new_current  (RWScreen       *screen);
+gboolean        configuration_match        (Configuration  *config1,
+					    Configuration  *config2);
+gboolean        configuration_save         (Configuration  *configuration,
+					    GError        **err);
+void		configuration_sanitize     (Configuration  *configuration);
+gboolean	configuration_apply_stored (RWScreen       *screen);
+gboolean	configuration_applicable   (Configuration  *configuration,
+					    RWScreen       *screen);
+
+#endif
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/randrwrap.h
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/libgnomeui/randrwrap.h	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,96 @@
+#ifndef RANDR_WRAP_H
+#define RANDR_WRAP_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+typedef struct RWScreen RWScreen;
+typedef struct RWOutput RWOutput;
+typedef struct RWCrtc RWCrtc;
+typedef struct RWMode RWMode;
+
+typedef void (* RWScreenChanged) (RWScreen *screen, gpointer data);
+
+typedef enum
+{
+    RW_ROTATION_0 =	(1 << 0),
+    RW_ROTATION_90 =	(1 << 1),
+    RW_ROTATION_180 =	(1 << 2),
+    RW_ROTATION_270 =	(1 << 3),
+    RW_REFLECT_X =	(1 << 4),
+    RW_REFLECT_Y =	(1 << 5)
+} RWRotation;
+
+/* RWScreen */
+RWScreen *    rw_screen_new                (GdkScreen       *screen,
+					    RWScreenChanged  callback,
+					    gpointer         data);
+RWOutput **   rw_screen_list_outputs       (RWScreen        *screen);
+RWCrtc **     rw_screen_list_crtcs         (RWScreen        *screen);
+RWMode **     rw_screen_list_modes         (RWScreen        *screen);
+void          rw_screen_set_size           (RWScreen        *screen,
+					    int		     width,
+					    int              height,
+					    int              mm_width,
+					    int              mm_height);
+RWCrtc *      rw_screen_get_crtc_by_id     (RWScreen        *screen,
+					    guint32          id);
+gboolean      rw_screen_refresh            (RWScreen        *screen);
+RWOutput *    rw_screen_get_output_by_id   (RWScreen        *screen,
+					    guint32          id);
+RWOutput *    rw_screen_get_output_by_name (RWScreen        *screen,
+					    const char      *name);
+void	      rw_screen_get_ranges         (RWScreen	    *screen,
+					    int		    *min_width,
+					    int		    *max_width,
+					    int             *min_height,
+					    int		    *max_height);
+
+/* RWOutput */
+guint32       rw_output_get_id             (RWOutput        *output);
+const char *  rw_output_get_name           (RWOutput        *output);
+gboolean      rw_output_is_connected       (RWOutput        *output);
+int           rw_output_get_size_inches    (RWOutput        *output);
+int           rw_output_get_width_mm       (RWOutput        *outout);
+int           rw_output_get_height_mm      (RWOutput        *output);
+const guint8 *rw_output_get_edid_data      (RWOutput        *output);
+RWCrtc **     rw_output_get_possible_crtcs (RWOutput        *output);
+RWMode *      rw_output_get_current_mode   (RWOutput        *output);
+RWCrtc *      rw_output_get_crtc           (RWOutput        *output);
+void          rw_output_get_position       (RWOutput        *output,
+					    int             *x,
+					    int             *y);
+gboolean      rw_output_can_clone          (RWOutput        *output,
+					    RWOutput        *clone);
+RWMode **     rw_output_list_modes         (RWOutput        *output);
+RWMode *      rw_output_get_preferred_mode (RWOutput        *output);
+gboolean      rw_output_supports_mode      (RWOutput        *output,
+					    RWMode          *mode);
+
+/* RWMode */
+guint32	      rw_mode_get_id               (RWMode          *mode);
+guint	      rw_mode_get_width		   (RWMode          *mode);
+guint         rw_mode_get_height           (RWMode          *mode);
+int           rw_mode_get_freq             (RWMode          *mode);
+
+/* RWCrtc */
+guint32       rw_crtc_get_id               (RWCrtc          *crtc);
+gboolean      rw_crtc_set_config	   (RWCrtc          *crtc,
+					    int		     x,
+					    int		     y,
+					    RWMode	    *mode,
+					    RWRotation	     rotation,
+					    RWOutput	   **outputs,
+				  	    int              n_outputs);
+gboolean      rw_crtc_can_drive_output     (RWCrtc          *crtc,
+					    RWOutput        *output);
+RWMode *      rw_crtc_get_current_mode     (RWCrtc          *crtc);
+void          rw_crtc_get_position         (RWCrtc          *crtc,
+					    int             *x,
+					    int             *y);
+RWRotation    rw_crtc_get_current_rotation (RWCrtc          *crtc);
+RWRotation    rw_crtc_get_rotations        (RWCrtc          *crtc);
+gboolean      rw_crtc_supports_rotation    (RWCrtc	    *crtc,
+					    RWRotation	     rotation);
+
+#endif
diff -up gnome-desktop-2.22.2/libgnome-desktop/Makefile.am.add-randr-12 gnome-desktop-2.22.2/libgnome-desktop/Makefile.am
--- gnome-desktop-2.22.2/libgnome-desktop/Makefile.am.add-randr-12	2008-05-26 12:10:57.000000000 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/Makefile.am	2008-05-28 09:28:15.000000000 -0400
@@ -20,7 +20,12 @@ libgnome_desktop_2_la_SOURCES = \
 	gnome-desktop-item.c	\
 	gnome-ditem-edit.c	\
 	gnome-hint.c		\
-	gnome-bg.c
+	gnome-bg.c		\
+ 	display-name.c		\
+ 	randrwrap.c		\
+ 	monitor-db.c		\
+ 	edid-parse.c		\
+ 	edid.h
 
 libgnome_desktop_2_la_LIBADD =	\
 	$(XLIB_LIBS)		\
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/monitor-db.c
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/monitor-db.c	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,1292 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libgnomeui/monitor-db.h"
+#include "edid.h"
+
+/* A helper wrapper around the GMarkup parser stuff */
+static gboolean parse_file_gmarkup (const gchar *file,
+				    const GMarkupParser *parser,
+				    gpointer data,
+				    GError **err);
+
+typedef struct CrtcAssignment CrtcAssignment;
+
+static void            crtc_assignment_apply (CrtcAssignment *assign);
+static CrtcAssignment *crtc_assignment_new   (RWScreen       *screen,
+					      Output        **outputs);
+static void            crtc_assignment_free  (CrtcAssignment *assign);
+static void            output_free           (Output         *output);
+static Output *	       output_copy	     (Output         *output);
+
+typedef struct Parser Parser;
+
+/* Parser for monitor configurations */
+struct Parser
+{
+    Output *output;
+    Configuration *configuration;
+    GPtrArray *outputs;
+    GPtrArray *configurations;
+    GQueue *stack;
+};
+
+static int
+parse_int (const char *text)
+{
+    return strtol (text, NULL, 0);
+}
+
+static guint
+parse_uint (const char *text)
+{
+    return strtoul (text, NULL, 0);
+}
+
+static gboolean
+stack_is (Parser *parser,
+	  const char *s1,
+	  ...)
+{
+    GList *stack = NULL;
+    const char *s;
+    GList *l1, *l2;
+    va_list args;
+    
+    stack = g_list_prepend (stack, (gpointer)s1);
+    
+    va_start (args, s1);
+    
+    s = va_arg (args, const char *);
+    while (s)
+    {
+	stack = g_list_prepend (stack, (gpointer)s);
+	s = va_arg (args, const char *);
+    }
+	
+    l1 = stack;
+    l2 = parser->stack->head;
+    
+    while (l1 && l2)
+    {
+	if (strcmp (l1->data, l2->data) != 0)
+	{
+	    g_list_free (stack);
+	    return FALSE;
+	}
+	
+	l1 = l1->next;
+	l2 = l2->next;
+    }
+    
+    g_list_free (stack);
+    
+    return (!l1 && !l2);
+}
+
+static void
+handle_start_element (GMarkupParseContext *context,
+		      const gchar         *name,
+		      const gchar        **attr_names,
+		      const gchar        **attr_values,
+		      gpointer             user_data,
+		      GError             **err)
+{
+    Parser *parser = user_data;
+
+    if (strcmp (name, "output") == 0)
+    {
+	int i;
+	g_assert (parser->output == NULL);
+
+	parser->output = g_new0 (Output, 1);
+	parser->output->rotation = 0;
+	
+	for (i = 0; attr_names[i] != NULL; ++i)
+	{
+	    if (strcmp (attr_names[i], "name") == 0)
+	    {
+		parser->output->name = g_strdup (attr_values[i]);
+		break;
+	    }
+	}
+
+	if (!parser->output->name)
+	{
+	    /* This really shouldn't happen, but it's better to make
+	     * something up than to crash later.
+	     */
+	    g_warning ("Malformed monitor configuration file");
+	    
+	    parser->output->name = g_strdup ("default");
+	}	
+	parser->output->connected = FALSE;
+	parser->output->on = FALSE;
+    }
+    else if (strcmp (name, "configuration") == 0)
+    {
+	g_assert (parser->configuration == NULL);
+	
+	parser->configuration = g_new0 (Configuration, 1);
+	parser->configuration->clone = FALSE;
+	parser->configuration->outputs = g_new0 (Output *, 1);
+    }
+
+    g_queue_push_tail (parser->stack, g_strdup (name));
+}
+
+static void
+handle_end_element (GMarkupParseContext *context,
+		    const gchar         *name,
+		    gpointer             user_data,
+		    GError             **err)
+{
+    Parser *parser = user_data;
+    
+    if (strcmp (name, "output") == 0)
+    {
+	/* If no rotation properties were set, just use RW_ROTATION_0 */
+	if (parser->output->rotation == 0)
+	    parser->output->rotation = RW_ROTATION_0;
+	
+	g_ptr_array_add (parser->outputs, parser->output);
+
+	parser->output = NULL;
+    }
+    else if (strcmp (name, "configuration") == 0)
+    {
+	g_ptr_array_add (parser->outputs, NULL);
+	parser->configuration->outputs =
+	    (Output **)g_ptr_array_free (parser->outputs, FALSE);
+	parser->outputs = g_ptr_array_new ();
+	g_ptr_array_add (parser->configurations, parser->configuration);
+	parser->configuration = NULL;
+    }
+    
+    g_free (g_queue_pop_tail (parser->stack));
+}
+
+static void
+handle_text (GMarkupParseContext *context,
+	     const gchar         *text,
+	     gsize                text_len,
+	     gpointer             user_data,
+	     GError             **err)
+{
+    Parser *parser = user_data;
+    
+    if (stack_is (parser, "vendor", "output", "configuration", NULL))
+    {
+	parser->output->connected = TRUE;
+	
+	strncpy (parser->output->vendor, text, 3);
+	parser->output->vendor[3] = 0;
+    }
+    else if (stack_is (parser, "clone", "configuration", NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	    parser->configuration->clone = TRUE;
+    }
+    else if (stack_is (parser, "product", "output", "configuration", NULL))
+    {
+	parser->output->connected = TRUE;
+
+	parser->output->product = parse_int (text);
+    }
+    else if (stack_is (parser, "serial", "output", "configuration", NULL))
+    {
+	parser->output->connected = TRUE;
+
+	parser->output->serial = parse_uint (text);
+    }
+    else if (stack_is (parser, "width", "output", "configuration", NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->width = parse_int (text);
+    }
+    else if (stack_is (parser, "x", "output", "configuration", NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->x = parse_int (text);
+    }
+    else if (stack_is (parser, "y", "output", "configuration", NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->y = parse_int (text);
+    }
+    else if (stack_is (parser, "height", "output", "configuration", NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->height = parse_int (text);
+    }
+    else if (stack_is (parser, "rate", "output", "configuration", NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->rate = parse_int (text);
+    }
+    else if (stack_is (parser, "rotation", "output", "configuration", NULL))
+    {
+	if (strcmp (text, "normal") == 0)
+	{
+	    parser->output->rotation |= RW_ROTATION_0;
+	}
+	else if (strcmp (text, "left") == 0)
+	{
+	    parser->output->rotation |= RW_ROTATION_90;
+	}
+	else if (strcmp (text, "upside_down") == 0)
+	{
+	    parser->output->rotation |= RW_ROTATION_180;
+	}
+	else if (strcmp (text, "right") == 0)
+	{
+	    parser->output->rotation |= RW_ROTATION_270;
+	}
+    }
+    else if (stack_is (parser, "reflect_x", "output", "configuration", NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	{
+	    parser->output->rotation |= RW_REFLECT_X;
+	}
+    }
+    else if (stack_is (parser, "reflect_y", "output", "configuration", NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	{
+	    parser->output->rotation |= RW_REFLECT_Y;
+	}
+    }
+    else
+    {
+	/* Ignore other properties so we can expand the format in the future */
+    }
+}
+
+static void
+parser_free (Parser *parser)
+{
+    int i;
+    GList *list;
+
+    g_assert (parser != NULL);
+
+    if (parser->output)
+	output_free (parser->output);
+
+    if (parser->configuration)
+	configuration_free (parser->configuration);
+
+    for (i = 0; i < parser->outputs->len; ++i)
+    {
+	Output *output = parser->outputs->pdata[i];
+
+	output_free (output);
+    }
+
+    g_ptr_array_free (parser->outputs, TRUE);
+
+    for (i = 0; i < parser->configurations->len; ++i)
+    {
+	Configuration *config = parser->configurations->pdata[i];
+
+	configuration_free (config);
+    }
+
+    g_ptr_array_free (parser->configurations, TRUE);
+
+    for (list = parser->stack->head; list; list = list->next)
+	g_free (list->data);
+    g_queue_free (parser->stack);
+    
+    g_free (parser);
+}
+
+static Configuration **
+configurations_read (const gchar *filename, GError **error)
+{
+    Parser *parser = g_new0 (Parser, 1);
+    Configuration **result;
+    GMarkupParser callbacks = {
+	handle_start_element,
+	handle_end_element,
+	handle_text,
+	NULL, /* passthrough */
+	NULL, /* error */
+    };
+
+    parser->configurations = g_ptr_array_new ();
+    parser->outputs = g_ptr_array_new ();
+    parser->stack = g_queue_new ();
+    
+    if (!parse_file_gmarkup (filename, &callbacks, parser, error))
+    {
+	result = NULL;
+	
+	g_assert (parser->outputs);
+	goto out;
+    }
+
+    g_assert (parser->outputs);
+    
+    g_ptr_array_add (parser->configurations, NULL);
+    result = (Configuration **)g_ptr_array_free (parser->configurations, FALSE);
+    parser->configurations = g_ptr_array_new ();
+    
+    g_assert (parser->outputs);
+out:
+    parser_free (parser);
+
+    return result;
+}
+
+Configuration *
+configuration_new_current (RWScreen *screen)
+{
+    Configuration *config = g_new0 (Configuration, 1);
+    GPtrArray *a = g_ptr_array_new ();
+    RWOutput **rw_outputs;
+    int i;
+    int clone_width = -1;
+    int clone_height = -1;
+
+    g_return_val_if_fail (screen != NULL, NULL);
+
+    rw_outputs = rw_screen_list_outputs (screen);
+
+    config->clone = FALSE;
+    
+    for (i = 0; rw_outputs[i] != NULL; ++i)
+    {
+	RWOutput *rw_output = rw_outputs[i];
+	Output *output = g_new0 (Output, 1);
+	RWMode *mode = NULL;
+	const guint8 *edid_data = rw_output_get_edid_data (rw_output);
+	RWCrtc *crtc;
+
+	output->name = g_strdup (rw_output_get_name (rw_output));
+	output->connected = rw_output_is_connected (rw_output);
+
+	if (!output->connected)
+	{
+	    output->x = -1;
+	    output->y = -1;
+	    output->width = -1;
+	    output->height = -1;
+	    output->rate = -1;
+	    output->rotation = RW_ROTATION_0;
+	}
+	else
+	{
+	    MonitorInfo *info = NULL;
+
+	    if (edid_data)
+		info = decode_edid (edid_data);
+
+	    if (info)
+	    {
+		memcpy (output->vendor, info->manufacturer_code,
+			sizeof (output->vendor));
+		
+		output->product = info->product_code;
+		output->serial = info->serial_number;
+		output->aspect = info->aspect_ratio;
+	    }
+	    else
+	    {
+		strcpy (output->vendor, "???");
+		output->product = 0;
+		output->serial = 0;
+	    }
+	    
+	    output->display_name = make_display_name (
+		rw_output_get_name (rw_output), info);
+		
+	    g_free (info);
+		
+	    crtc = rw_output_get_crtc (rw_output);
+	    mode = crtc? rw_crtc_get_current_mode (crtc) : NULL;
+	    
+	    if (crtc && mode)
+	    {
+		output->on = TRUE;
+		
+		rw_crtc_get_position (crtc, &output->x, &output->y);
+		output->width = rw_mode_get_width (mode);
+		output->height = rw_mode_get_height (mode);
+		output->rate = rw_mode_get_freq (mode);
+		output->rotation = rw_crtc_get_current_rotation (crtc);
+
+		if (output->x == 0 && output->y == 0) {
+			if (clone_width == -1) {
+				clone_width = output->width;
+				clone_height = output->height;
+			} else if (clone_width == output->width &&
+					clone_height == output->height) {
+				config->clone = TRUE;
+			}
+		}
+	    }
+	    else
+	    {
+		output->on = FALSE;
+		config->clone = FALSE;
+	    }
+
+	    /* Get preferred size for the monitor */
+	    mode = rw_output_get_preferred_mode (rw_output);
+	    
+	    if (!mode)
+	    {
+		RWMode **modes = rw_output_list_modes (rw_output);
+		
+		/* FIXME: we should pick the "best" mode here, where best is
+		 * sorted wrt
+		 *
+		 * - closest aspect ratio
+		 * - mode area
+		 * - refresh rate
+		 * - We may want to extend randrwrap so that get_preferred
+		 *   returns that - although that could also depend on
+		 *   the crtc.
+		 */
+		if (modes[0])
+		    mode = modes[0];
+	    }
+	    
+	    if (mode)
+	    {
+		output->pref_width = rw_mode_get_width (mode);
+		output->pref_height = rw_mode_get_height (mode);
+	    }
+	    else
+	    {
+		/* Pick some random numbers. This should basically never happen */
+		output->pref_width = 1024;
+		output->pref_height = 768;
+	    }
+	}
+ 
+	g_ptr_array_add (a, output);
+    }
+
+    g_ptr_array_add (a, NULL);
+    
+    config->outputs = (Output **)g_ptr_array_free (a, FALSE);
+
+    g_assert (configuration_match (config, config));
+    
+    return config;
+}
+
+static void
+output_free (Output *output)
+{
+    if (output->display_name)
+	g_free (output->display_name);
+
+    if (output->name)
+	g_free (output->name);
+    
+    g_free (output);
+}
+
+static Output *
+output_copy (Output *output)
+{
+    Output *copy = g_new0 (Output, 1);
+
+    *copy = *output;
+
+    copy->name = g_strdup (output->name);
+    copy->display_name = g_strdup (output->display_name);
+
+    return copy;
+}
+
+static void
+outputs_free (Output **outputs)
+{
+    int i;
+
+    g_assert (outputs != NULL);
+
+    for (i = 0; outputs[i] != NULL; ++i)
+	output_free (outputs[i]);
+}
+
+void
+configuration_free (Configuration *config)
+{
+    g_return_if_fail (config != NULL);
+    outputs_free (config->outputs);
+    
+    g_free (config);
+}
+
+static void
+configurations_free (Configuration **configurations)
+{
+    int i;
+
+    g_assert (configurations != NULL);
+
+    for (i = 0; configurations[i] != NULL; ++i)
+	configuration_free (configurations[i]);
+
+    g_free (configurations);
+}
+
+static gboolean
+parse_file_gmarkup (const gchar *filename,
+		    const GMarkupParser *parser,
+		    gpointer data,
+		    GError **err)
+{
+    GMarkupParseContext *context = NULL;
+    gchar *contents = NULL;
+    gboolean result = TRUE;
+    gsize len;
+
+    if (!g_file_get_contents (filename, &contents, &len, err))
+    {
+	result = FALSE;
+	goto out;
+    }
+    
+    context = g_markup_parse_context_new (parser, 0, data, NULL);
+
+    if (!g_markup_parse_context_parse (context, contents, len, err))
+    {
+	result = FALSE;
+	goto out;
+    }
+
+    if (!g_markup_parse_context_end_parse (context, err))
+    {
+	result = FALSE;
+	goto out;
+    }
+
+out:
+    if (contents)
+	g_free (contents);
+
+    if (context)
+	g_markup_parse_context_free (context);
+
+    return result;
+}
+
+static gboolean
+output_match (Output *output1, Output *output2)
+{
+    g_assert (output1 != NULL);
+    g_assert (output2 != NULL);
+
+    if (strcmp (output1->name, output2->name) != 0)
+	return FALSE;
+
+    if (strcmp (output1->vendor, output2->vendor) != 0)
+	return FALSE;
+
+    if (output1->product != output2->product)
+	return FALSE;
+
+    if (output1->serial != output2->serial)
+	return FALSE;
+
+    if (output1->connected != output2->connected)
+	return FALSE;
+    
+    return TRUE;
+}
+
+static Output *
+find_output (Configuration *config, const char *name)
+{
+    int i;
+
+    for (i = 0; config->outputs[i] != NULL; ++i)
+    {
+	Output *output = config->outputs[i];
+	
+	if (strcmp (name, output->name) == 0)
+	    return output;
+    }
+
+    return NULL;
+}
+
+gboolean
+configuration_match (Configuration *c1, Configuration *c2)
+{
+    int i;
+
+    for (i = 0; c1->outputs[i] != NULL; ++i)
+    {
+	Output *output1 = c1->outputs[i];
+	Output *output2;
+
+	output2 = find_output (c2, output1->name);
+	if (!output2 || !output_match (output1, output2))
+	    return FALSE;
+    }
+    
+    return TRUE;
+}
+
+static Output **
+make_outputs (Configuration *config)
+{
+    GPtrArray *outputs;
+    Output *first_on;;
+    int i;
+
+    outputs = g_ptr_array_new ();
+
+    first_on = NULL;
+    
+    for (i = 0; config->outputs[i] != NULL; ++i)
+    {
+	Output *old = config->outputs[i];
+	Output *new = output_copy (old);
+
+	if (old->on && !first_on)
+	    first_on = old;
+	
+	if (config->clone && new->on)
+	{
+	    g_assert (first_on);
+
+	    new->width = first_on->width;
+	    new->height = first_on->height;
+	    new->rotation = first_on->rotation;
+	    new->x = 0;
+	    new->y = 0;
+	}
+
+	g_ptr_array_add (outputs, new);
+    }
+
+    g_ptr_array_add (outputs, NULL);
+
+    return (Output **)g_ptr_array_free (outputs, FALSE);
+}
+
+gboolean
+configuration_applicable (Configuration  *configuration,
+			  RWScreen       *screen)
+{
+    Output **outputs = make_outputs (configuration);
+    CrtcAssignment *assign = crtc_assignment_new (screen, outputs);
+    gboolean result;
+
+    if (assign)
+    {
+	result = TRUE;
+	crtc_assignment_free (assign);
+    }
+    else
+    {
+	result = FALSE;
+    }
+
+    outputs_free (outputs);
+
+    return result;
+}
+
+static Configuration *
+configuration_find (Configuration **haystack,
+		    Configuration  *needle)
+{
+    int i;
+
+    for (i = 0; haystack[i] != NULL; ++i)
+    {
+	if (configuration_match (haystack[i], needle))
+	    return haystack[i];
+    }
+
+    return NULL;
+}
+
+/* Database management */
+static gboolean
+do_free (gpointer data)
+{
+    g_free (data);
+    return FALSE;
+}
+
+static gchar *
+idle_free (gchar *s)
+{
+    g_idle_add (do_free, s);
+
+    return s;
+}
+
+static const gchar *
+get_filename (void)
+{
+    return idle_free (
+	g_build_filename (
+	    g_get_home_dir(), ".gnome2", "monitors.xml", NULL));
+}
+
+static const char *
+get_rotation_name (RWRotation r)
+{
+    if (r & RW_ROTATION_0)
+	return "normal";
+    if (r & RW_ROTATION_90)
+	return "left";
+    if (r & RW_ROTATION_180)
+	return "upside_down";
+    if (r & RW_ROTATION_270)
+	return "right";
+
+    return "normal";
+}
+
+static const char *
+yes_no (int x)
+{
+    return x? "yes" : "no";
+}
+
+static const char *
+get_reflect_x (RWRotation r)
+{
+    return yes_no (r & RW_REFLECT_X);
+}
+
+static const char *
+get_reflect_y (RWRotation r)
+{
+    return yes_no (r & RW_REFLECT_Y);
+}
+
+static void
+emit_configuration (Configuration *config,
+		    GString *string)
+{
+    int j;
+	
+    g_string_append_printf (string, "<configuration>\n");
+
+    g_string_append_printf (string, "    <clone>%s</clone>\n", yes_no (config->clone));
+    
+    for (j = 0; config->outputs[j] != NULL; ++j)
+    {
+	Output *output = config->outputs[j];
+	
+	g_string_append_printf (
+	    string, "    <output name=\"%s\">\n", output->name);
+	
+	if (output->connected && *output->vendor != '\0')
+	{
+	    g_string_append_printf (
+		string, "        <vendor>%s</vendor>\n", output->vendor);
+	    g_string_append_printf (
+		string, "        <product>0x%04x</product>\n", output->product);
+	    g_string_append_printf (
+		string, "        <serial>0x%08x</serial>\n", output->serial);
+	}
+	
+	/* An unconnected output which is on does not make sense */
+	if (output->connected && output->on)
+	{
+	    g_string_append_printf (
+		string, "        <width>%d</width>\n", output->width);
+	    g_string_append_printf (
+		string, "        <height>%d</height>\n", output->height);
+	    g_string_append_printf (
+		string, "        <rate>%d</rate>\n", output->rate);
+	    g_string_append_printf (
+		string, "        <x>%d</x>\n", output->x);
+	    g_string_append_printf (
+		string, "        <y>%d</y>\n", output->y);
+	    g_string_append_printf (
+		string, "        <rotation>%s</rotation>\n", get_rotation_name (output->rotation));
+	    g_string_append_printf (
+		string, "        <reflect_x>%s</reflect_x>\n", get_reflect_x (output->rotation));
+	    g_string_append_printf (
+		string, "        <reflect_y>%s</reflect_y>\n", get_reflect_y (output->rotation));
+	}
+	
+	g_string_append_printf (string, "    </output>\n");
+    }
+    
+    g_string_append_printf (string, "</configuration>\n");
+}
+
+void
+configuration_sanitize (Configuration *config)
+{
+    int i;
+    int x_offset, y_offset;
+
+    /* Offset everything by the top/left-most coordinate to
+     * make sure the configuration starts at (0, 0)
+     */
+    x_offset = y_offset = G_MAXINT;
+    for (i = 0; config->outputs[i]; ++i)
+    {
+	Output *output = config->outputs[i];
+
+	if (output->on)
+	{
+	    x_offset = MIN (x_offset, output->x);
+	    y_offset = MIN (y_offset, output->y);
+	}
+    }
+
+    for (i = 0; config->outputs[i]; ++i)
+    {
+	Output *output = config->outputs[i];
+	
+	if (output->on)
+	{
+	    output->x -= x_offset;
+	    output->y -= y_offset;
+	}
+    }
+}
+
+gboolean
+configuration_save (Configuration *configuration, GError **err)
+{
+    Configuration **configurations;
+    GString *output = g_string_new("");
+    int i;
+
+    configurations = configurations_read (get_filename(), NULL);
+    
+    if (configurations)
+    {
+	for (i = 0; configurations[i] != NULL; ++i)
+	{
+	    if (!configuration_match (configurations[i], configuration))
+		emit_configuration (configurations[i], output);
+	}
+
+	configurations_free (configurations);
+    }
+
+    emit_configuration (configuration, output);
+
+    return g_file_set_contents (get_filename(), output->str, -1, err);
+}
+
+static gboolean
+apply_configuration (Configuration *conf, RWScreen *screen)
+{
+    CrtcAssignment *assignment;
+    Output **outputs;
+
+    outputs = make_outputs (conf);
+
+    assignment = crtc_assignment_new (screen, outputs);
+
+    outputs_free (outputs);
+    
+    if (assignment)
+    {
+	crtc_assignment_apply (assignment);
+	    
+	crtc_assignment_free (assignment);
+
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+gboolean
+configuration_apply_stored (RWScreen *screen)
+{
+    char *file = g_build_filename (
+	g_get_home_dir(), ".gnome2", "monitors.xml", NULL);
+    Configuration **configs = configurations_read (file, NULL);
+    Configuration *current;
+    Configuration *found;
+    gboolean result;
+
+    if (!screen)
+	return FALSE;
+    
+    rw_screen_refresh (screen);
+    
+    current = configuration_new_current (screen);
+    if (configs)
+    {
+	if ((found = configuration_find (configs, current)))
+	{
+	    apply_configuration (found, screen);
+	    result = TRUE;
+	}
+	else
+	{
+	    result = FALSE;
+	}
+	
+	configurations_free (configs);
+    }
+	
+    g_free (file);
+    configuration_free (current);
+
+    return result;
+}
+
+
+/*
+ * CRTC assignment
+ */
+typedef struct CrtcInfo CrtcInfo;
+
+struct CrtcInfo
+{
+    RWMode    *mode;
+    int        x;
+    int        y;
+    RWRotation rotation;
+    GPtrArray *outputs;
+};
+
+struct CrtcAssignment
+{
+    RWScreen *screen;
+    GHashTable *info;
+};
+
+static gboolean
+can_clone (CrtcInfo *info,
+	   RWOutput *output)
+{
+    int i;
+
+    for (i = 0; i < info->outputs->len; ++i)
+    {
+	RWOutput *clone = info->outputs->pdata[i];
+
+	if (!rw_output_can_clone (clone, output))
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+crtc_assignment_assign (CrtcAssignment *assign,
+			RWCrtc         *crtc,
+			RWMode         *mode,
+			int             x,
+			int             y,
+			RWRotation      rotation,
+			RWOutput       *output)
+{
+    /* FIXME: We should reject stuff that is outside the screen ranges */
+    
+    CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+    if (!rw_crtc_can_drive_output (crtc, output) ||
+	!rw_output_supports_mode (output, mode)  ||
+	!rw_crtc_supports_rotation (crtc, rotation))
+    {
+	return FALSE;
+    }
+
+    if (info)
+    {
+	if (info->mode == mode		&&
+	    info->x == x		&&
+	    info->y == y		&&
+	    info->rotation == rotation  &&
+	    can_clone (info, output))
+	{
+	    g_ptr_array_add (info->outputs, output);
+
+	    return TRUE;
+	}
+    }
+    else
+    {	
+	CrtcInfo *info = g_new0 (CrtcInfo, 1);
+	
+	info->mode = mode;
+	info->x = x;
+	info->y = y;
+	info->rotation = rotation;
+	info->outputs = g_ptr_array_new ();
+	
+	g_ptr_array_add (info->outputs, output);
+	
+	g_hash_table_insert (assign->info, crtc, info);
+	    
+	return TRUE;
+    }
+    
+    return FALSE;
+}
+
+static void
+crtc_assignment_unassign (CrtcAssignment *assign,
+			  RWCrtc         *crtc,
+			  RWOutput       *output)
+{
+    CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+    if (info)
+    {
+	g_ptr_array_remove (info->outputs, output);
+
+	if (info->outputs->len == 0)
+	    g_hash_table_remove (assign->info, crtc);
+    }
+}
+
+static void
+crtc_assignment_free (CrtcAssignment *assign)
+{
+    g_hash_table_destroy (assign->info);
+
+    g_free (assign);
+}
+
+static void
+configure_crtc (gpointer key,
+		gpointer value,
+		gpointer data)
+{
+    RWCrtc *crtc = key;
+    CrtcInfo *info = value;
+
+    rw_crtc_set_config (crtc,
+			info->x, info->y,
+			info->mode,
+			info->rotation,
+			(RWOutput **)info->outputs->pdata,
+			info->outputs->len);
+}
+
+static gboolean
+mode_is_rotated (CrtcInfo *info)
+{
+    if ((info->rotation & RW_ROTATION_270)		||
+	(info->rotation & RW_ROTATION_90))
+    {
+	return TRUE;
+    }
+    return FALSE;
+}
+
+static gboolean
+crtc_is_rotated (RWCrtc *crtc)
+{
+    RWRotation r = rw_crtc_get_current_rotation (crtc);
+
+    if ((r & RW_ROTATION_270)		||
+	(r & RW_ROTATION_90))
+    {
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+crtc_assignment_apply (CrtcAssignment *assign)
+{
+    GList *active_crtcs = g_hash_table_get_keys (assign->info);
+    RWCrtc **all_crtcs = rw_screen_list_crtcs (assign->screen);
+    GList *list;
+    int width, height;
+    int i;
+    int min_width, max_width, min_height, max_height;
+    int width_mm, height_mm;
+
+    /* Compute size of the screen */
+    width = height = 1;
+    for (list = active_crtcs; list != NULL; list = list->next)
+    {
+	RWCrtc *crtc = list->data;
+	CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+	int w, h;
+
+	w = rw_mode_get_width (info->mode);
+	h = rw_mode_get_height (info->mode);
+	
+	if (mode_is_rotated (info))
+	{
+	    int tmp = h;
+	    h = w;
+	    w = tmp;
+	}
+	
+	width = MAX (width, info->x + w);
+	height = MAX (height, info->y + h);
+    }
+    g_list_free (active_crtcs);
+
+    rw_screen_get_ranges (
+	assign->screen, &min_width, &max_width, &min_height, &max_height);
+
+    width = MAX (min_width, width);
+    width = MIN (max_width, width);
+    height = MAX (min_height, height);
+    height = MIN (max_height, height);
+    
+    /* Turn off all crtcs currently displaying outside the new screen */
+    for (i = 0; all_crtcs[i] != NULL; ++i)
+    {
+	RWCrtc *crtc = all_crtcs[i];
+	RWMode *mode = rw_crtc_get_current_mode (crtc);
+	int x, y;
+
+	if (mode)
+	{
+	    int w, h;
+	    rw_crtc_get_position (crtc, &x, &y);
+
+	    w = rw_mode_get_width (mode);
+	    h = rw_mode_get_height (mode);
+	    
+	    if (crtc_is_rotated (crtc))
+	    {
+		int tmp = h;
+		h = w;
+		w = tmp;
+	    }
+	    
+	    if (x + w > width || y + h > height)
+		rw_crtc_set_config (crtc, 0, 0, NULL, RW_ROTATION_0, NULL, 0);
+	}
+    }
+
+    /* Turn off all CRTC's that are not in the assignment */
+    for (i = 0; all_crtcs[i] != NULL; ++i)
+    {
+	RWCrtc *crtc = all_crtcs[i];
+	
+	if (!g_hash_table_lookup (assign->info, crtc))
+	    rw_crtc_set_config (crtc, 0, 0, NULL, RW_ROTATION_0, NULL, 0);
+    }
+
+    /* The 'physical size' of an X screen is meaningless if that screen
+     * can consist of many monitors. So just pick a size that make the
+     * dpi 96.
+     *
+     * Firefox and Evince apparently believe what X tells them.
+     */
+    width_mm = (width / 96.0) * 25.4 + 0.5;
+    height_mm = (height / 96.0) * 25.4 + 0.5;
+    
+    rw_screen_set_size (assign->screen, width, height, width_mm, height_mm);
+
+    g_hash_table_foreach (assign->info, configure_crtc, NULL);
+}
+
+/* Check whether the given set of settings can be used
+ * at the same time -- ie. whether there is an assignment
+ * of CRTC's to outputs.
+ *
+ * Brute force - the number of objects involved is small
+ * enough that it doesn't matter.
+ */
+static gboolean
+real_assign_crtcs (RWScreen *screen,
+		   Output **outputs,
+		   CrtcAssignment *assignment)
+{
+    RWCrtc **crtcs = rw_screen_list_crtcs (screen);
+    Output *output;
+    int i;
+
+    output = *outputs;
+    if (!output)
+	return TRUE;
+
+    /* It is always allowed for an output to be turned off */
+    if (!output->on)
+    {
+	return real_assign_crtcs (screen, outputs + 1, assignment);
+    }
+
+    for (i = 0; crtcs[i] != NULL; ++i)
+    {
+	int pass;
+
+	/* Make two passses, one where frequencies must match, then
+	 * one where they don't have to
+	 */
+	for (pass = 0; pass < 2; ++pass)
+	{
+	    RWCrtc *crtc = crtcs[i];
+	    RWOutput *rw_output = rw_screen_get_output_by_name (screen, output->name);
+	    RWMode **modes = rw_output_list_modes (rw_output);
+	    int j;
+	
+	    for (j = 0; modes[j] != NULL; ++j)
+	    {
+		RWMode *mode = modes[j];
+		
+		if (rw_mode_get_width (mode) == output->width	&&
+		    rw_mode_get_height (mode) == output->height &&
+		    (pass == 1 || rw_mode_get_freq (mode) == output->rate))
+		{
+		    if (crtc_assignment_assign (
+			    assignment, crtc, modes[j],
+			    output->x, output->y,
+			    output->rotation,
+			    rw_output))
+		    {
+			if (real_assign_crtcs (screen, outputs + 1, assignment))
+			    return TRUE;
+			
+			crtc_assignment_unassign (assignment, crtc, rw_output);
+		    }
+		}
+	    }
+	}
+    }
+
+    return FALSE;
+}
+
+static void
+crtc_info_free (CrtcInfo *info)
+{
+    g_ptr_array_free (info->outputs, TRUE);
+    g_free (info);
+}
+
+static CrtcAssignment *
+crtc_assignment_new (RWScreen *screen, Output **outputs)
+{
+    CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
+
+    assignment->info = g_hash_table_new_full (
+	g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
+
+    if (real_assign_crtcs (screen, outputs, assignment))
+    {
+	assignment->screen = screen;
+	
+	return assignment;
+    }
+    else
+    {
+	crtc_assignment_free (assignment);
+    
+	return NULL;
+    }
+}
diff -up /dev/null gnome-desktop-2.22.2/libgnome-desktop/randrwrap.c
--- /dev/null	2008-05-28 08:27:37.811005639 -0400
+++ gnome-desktop-2.22.2/libgnome-desktop/randrwrap.c	2008-05-28 09:28:15.000000000 -0400
@@ -0,0 +1,1195 @@
+#include "libgnomeui/randrwrap.h"
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#define DISPLAY(o) ((o)->info->screen->xdisplay)
+
+typedef struct ScreenInfo ScreenInfo;
+
+struct ScreenInfo
+{
+    int			min_width;
+    int			max_width;
+    int			min_height;
+    int			max_height;
+
+    XRRScreenResources *resources;
+    
+    RWOutput **		outputs;
+    RWCrtc **		crtcs;
+    RWMode **		modes;
+
+    RWScreen *		screen;
+};
+
+struct RWScreen
+{
+    GdkScreen *		gdk_screen;
+    GdkWindow *		gdk_root;
+    Display *		xdisplay;
+    Screen *		xscreen;
+    Window		xroot;
+    ScreenInfo *	info;
+   
+    int			randr_event_base;
+
+    RWScreenChanged	callback;
+    gpointer		data;
+};
+
+struct RWOutput
+{
+    ScreenInfo *	info;
+    RROutput		id;
+    
+    char *		name;
+    RWCrtc *		current_crtc;
+    gboolean		connected;
+    gulong		width_mm;
+    gulong		height_mm;
+    RWCrtc **		possible_crtcs;
+    RWOutput **		clones;
+    RWMode **		modes;
+    int			n_preferred;
+    guint8 *		edid_data;
+};
+
+struct RWOutputWrap
+{
+    RROutput		id;
+};
+
+struct RWCrtc
+{
+    ScreenInfo *	info;
+    RRCrtc		id;
+    
+    RWMode *		current_mode;
+    RWOutput **		current_outputs;
+    RWOutput **		possible_outputs;
+    int			x;
+    int			y;
+
+    RWRotation		current_rotation;
+    RWRotation		rotations;
+};
+
+struct RWMode
+{
+    ScreenInfo *	info;
+    RRMode		id;
+    char *		name;
+    int			width;
+    int			height;
+    int			freq;		/* in mHz */
+};
+
+/* RWCrtc */
+static RWCrtc *  crtc_new             (ScreenInfo         *info,
+				       RRCrtc              id);
+static void      crtc_free            (RWCrtc             *crtc);
+static void      crtc_initialize      (RWCrtc             *crtc,
+				       XRRScreenResources *res);
+
+
+/* RWOutput */
+static RWOutput *output_new           (ScreenInfo         *info,
+				       RROutput            id);
+static void      output_initialize    (RWOutput           *output,
+				       XRRScreenResources *res);
+static void      output_free          (RWOutput           *output);
+
+
+/* RWMode */
+static RWMode *  mode_new             (ScreenInfo         *info,
+				       RRMode              id);
+static void      mode_initialize      (RWMode             *mode,
+				       XRRModeInfo        *info);
+static void      mode_free            (RWMode             *mode);
+
+
+/* Screen */
+static RWOutput *
+rw_output_by_id (ScreenInfo *info, RROutput id)
+{
+    RWOutput **output;
+
+    g_assert (info != NULL);
+
+    for (output = info->outputs; *output; ++output)
+    {
+	if ((*output)->id == id)
+	    return *output;
+    }
+    
+    return NULL;
+}
+
+static RWCrtc *
+crtc_by_id (ScreenInfo *info, RRCrtc id)
+{
+    RWCrtc **crtc;
+
+    if (!info)
+        return NULL;
+
+    for (crtc = info->crtcs; *crtc; ++crtc)
+    {
+	if ((*crtc)->id == id)
+	    return *crtc;
+    }
+    
+    return NULL;
+}
+
+static RWMode *
+mode_by_id (ScreenInfo *info, RRMode id)
+{
+    RWMode **mode;
+
+    g_assert (info != NULL);
+
+    for (mode = info->modes; *mode; ++mode)
+    {
+	if ((*mode)->id == id)
+	    return *mode;
+    }
+    
+    return NULL;
+}
+
+static void
+screen_info_free (ScreenInfo *info)
+{
+    RWOutput **output;
+    RWCrtc **crtc;
+    RWMode **mode;
+
+    g_assert (info != NULL);
+
+    if (info->resources)
+    {
+	XRRFreeScreenResources (info->resources);
+
+	info->resources = NULL;
+    }
+    
+    if (info->outputs)
+    {
+	for (output = info->outputs; *output; ++output)
+	    output_free (*output);
+	g_free (info->outputs);
+    }
+    
+    if (info->crtcs)
+    {
+	for (crtc = info->crtcs; *crtc; ++crtc)
+	    crtc_free (*crtc);
+	g_free (info->crtcs);
+    }
+    
+    if (info->modes)
+    {
+	for (mode = info->modes; *mode; ++mode)
+	    mode_free (*mode);
+	g_free (info->modes);
+    }
+
+    g_free (info);
+}
+
+static gboolean
+fill_out_screen_info (Display *xdisplay, Window xroot,
+		      ScreenInfo *info)
+{
+    XRRScreenResources *resources;
+
+    g_assert (xdisplay != NULL);
+    g_assert (info != NULL);
+
+    gdk_error_trap_push ();
+    
+    if (!XRRGetScreenSizeRange (xdisplay, xroot,
+                                &(info->min_width),
+                                &(info->min_height),
+                                &(info->max_width),
+                                &(info->max_height))) {
+        /* XRR caught an error */
+        return False;
+    }
+    
+    gdk_flush ();
+    if (gdk_error_trap_pop ())
+    {
+        /* Unhandled X Error was generated */
+        return False;
+    }
+
+#if 0
+    g_print ("ranges: %d - %d; %d - %d\n",
+	     screen->min_width, screen->max_width,
+	     screen->min_height, screen->max_height);
+#endif
+    
+    resources = XRRGetScreenResources (xdisplay, xroot);
+
+    if (resources)
+    {
+	int i;
+	GPtrArray *a;
+	RWCrtc **crtc;
+	RWOutput **output;
+
+#if 0
+	g_print ("Resource Timestamp: %u\n", (guint32)resources->timestamp);
+	g_print ("Resource Configuration Timestamp: %u\n", (guint32)resources->configTimestamp);
+#endif
+	
+	info->resources = resources;
+	
+	/* We create all the structures before initializing them, so
+	 * that they can refer to each other.
+	 */
+	a = g_ptr_array_new ();
+	for (i = 0; i < resources->ncrtc; ++i)
+	{
+	    RWCrtc *crtc = crtc_new (info, resources->crtcs[i]);
+	    
+	    g_ptr_array_add (a, crtc);
+	}
+	g_ptr_array_add (a, NULL);
+	info->crtcs = (RWCrtc **)g_ptr_array_free (a, FALSE);
+	
+	a = g_ptr_array_new ();
+	for (i = 0; i < resources->noutput; ++i)
+	{
+	    RWOutput *output = output_new (info, resources->outputs[i]);
+	    
+	    g_ptr_array_add (a, output);
+	}
+	g_ptr_array_add (a, NULL);
+	info->outputs = (RWOutput **)g_ptr_array_free (a, FALSE);
+	
+	a = g_ptr_array_new ();
+	for (i = 0;  i < resources->nmode; ++i)
+	{
+	    RWMode *mode = mode_new (info, resources->modes[i].id);
+	    
+	    g_ptr_array_add (a, mode);
+	}
+	g_ptr_array_add (a, NULL);
+	info->modes = (RWMode **)g_ptr_array_free (a, FALSE);
+	
+	/* Initialize */
+	for (crtc = info->crtcs; *crtc; ++crtc)
+	    crtc_initialize (*crtc, resources);
+	
+	for (output = info->outputs; *output; ++output)
+	    output_initialize (*output, resources);
+	
+	for (i = 0; i < resources->nmode; ++i)
+	{
+	    RWMode *mode = mode_by_id (info, resources->modes[i].id);
+	    
+	    mode_initialize (mode, &(resources->modes[i]));
+	}
+
+	return TRUE;
+    }
+    else
+    {
+	g_print ("Couldn't get screen resources\n");
+
+	return FALSE;
+    }
+}
+
+static ScreenInfo *
+screen_info_new (RWScreen *screen)
+{
+    ScreenInfo *info = g_new0 (ScreenInfo, 1);
+    RWOutput **o;
+
+    g_assert (screen != NULL);
+
+    info->outputs = NULL;
+    info->crtcs = NULL;
+    info->modes = NULL;
+    info->screen = screen;
+
+    if (fill_out_screen_info (screen->xdisplay, screen->xroot, info))
+    {
+	return info;
+    }
+    else
+    {
+	g_free (info);
+	return NULL;
+    }
+
+    for (o = info->outputs; *o; o++)
+    {
+	
+    }
+    
+}
+
+static gboolean
+screen_update (RWScreen *screen, gboolean force_callback)
+{
+    ScreenInfo *info;
+    gboolean changed = FALSE;
+    
+    g_assert (screen != NULL);
+
+    info = screen_info_new (screen);
+    if (info)
+    {
+	if (info->resources->configTimestamp != screen->info->resources->configTimestamp)
+	    changed = TRUE;
+	
+	screen_info_free (screen->info);
+	
+	screen->info = info;
+    }
+    
+    if ((changed || force_callback) && screen->callback)
+	screen->callback (screen, screen->data);
+
+    return changed;
+}
+
+static GdkFilterReturn
+screen_on_event (GdkXEvent *xevent,
+		 GdkEvent *event,
+		 gpointer data)
+{
+    RWScreen *screen = data;
+    XEvent *e = xevent;
+    
+    if (e && e->type - screen->randr_event_base == RRNotify)
+    {
+	XRRNotifyEvent *event = (XRRNotifyEvent *)e;
+
+	switch (event->subtype)
+	{
+	default:
+	    break;
+	}
+	
+	/* FIXME: we may need to be more discriminating in
+	 * what causes 'changed' events
+	 */
+	screen_update (screen, TRUE);
+    }
+    
+    /* Pass the event on to GTK+ */
+    return GDK_FILTER_CONTINUE;
+}
+
+/* Returns NULL if screen could not be created.  For instance, if driver
+ * does not support Xrandr 1.2.
+ */
+RWScreen *
+rw_screen_new (GdkScreen *gdk_screen,
+	       RWScreenChanged callback,
+	       gpointer data)
+{
+    Display *dpy = GDK_SCREEN_XDISPLAY (gdk_screen);
+    int event_base;
+    int ignore;
+    
+    if (XRRQueryExtension (dpy, &event_base, &ignore))
+    {
+	RWScreen *screen = g_new0 (RWScreen, 1);
+
+	screen->gdk_screen = gdk_screen;
+	screen->gdk_root = gdk_screen_get_root_window (gdk_screen);
+	screen->xroot = gdk_x11_drawable_get_xid (screen->gdk_root);
+	screen->xdisplay = dpy;
+	screen->xscreen = gdk_x11_screen_get_xscreen (screen->gdk_screen);
+
+	screen->callback = callback;
+	screen->data = data;
+	
+	screen->randr_event_base = event_base;
+
+	screen->info = screen_info_new (screen);
+
+	if (!screen->info)
+	    return NULL;
+	
+	XRRSelectInput (screen->xdisplay,
+			screen->xroot,
+			RRScreenChangeNotifyMask	|
+			RRCrtcChangeNotifyMask		|
+			RROutputPropertyNotifyMask);
+	
+	gdk_x11_register_standard_event_type (
+	    gdk_screen_get_display (gdk_screen),
+	    event_base,
+	    RRNotify + 1);
+
+	gdk_window_add_filter (screen->gdk_root, screen_on_event, screen);
+	return screen;
+    }
+    
+    return NULL;
+}
+
+void
+rw_screen_set_size (RWScreen *screen,
+		    int	      width,
+		    int       height,
+		    int       mm_width,
+		    int       mm_height)
+{
+    g_return_if_fail (screen != NULL);
+    
+    XRRSetScreenSize (screen->xdisplay, screen->xroot,
+		      width, height, mm_width, mm_height);
+}
+
+void
+rw_screen_get_ranges (RWScreen	*screen,
+		      int	*min_width,
+		      int	*max_width,
+		      int       *min_height,
+		      int	*max_height)
+{
+    g_return_if_fail (screen != NULL);
+
+    if (min_width)
+	*min_width = screen->info->min_width;
+
+    if (max_width)
+	*max_width = screen->info->max_width;
+
+    if (min_height)
+	*min_height = screen->info->min_height;
+
+    if (max_height)
+	*max_height = screen->info->max_height;
+}
+
+gboolean
+rw_screen_refresh (RWScreen *screen)
+{
+    return screen_update (screen, FALSE);
+}
+
+RWMode **
+rw_screen_list_modes (RWScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    return screen->info->modes;
+}
+
+RWCrtc **
+rw_screen_list_crtcs (RWScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    return screen->info->crtcs;
+}
+
+RWOutput **
+rw_screen_list_outputs (RWScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    return screen->info->outputs;
+}
+
+RWCrtc *
+rw_screen_get_crtc_by_id (RWScreen *screen,
+			  guint32   id)
+{
+    int i;
+
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    for (i = 0; screen->info->crtcs[i] != NULL; ++i)
+    {
+	if (screen->info->crtcs[i]->id == id)
+	    return screen->info->crtcs[i];
+    }
+
+    return NULL;
+}
+
+RWOutput *
+rw_screen_get_output_by_id (RWScreen *screen,
+			    guint32 id)
+{
+    int i;
+
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    for (i = 0; screen->info->outputs[i] != NULL; ++i)
+    {
+	if (screen->info->outputs[i]->id == id)
+	    return screen->info->outputs[i];
+    }
+
+    return NULL;
+}
+
+/* RWOutput */
+static RWOutput *
+output_new (ScreenInfo *info, RROutput id)
+{
+    RWOutput *output = g_new0 (RWOutput, 1);
+
+    output->id = id;
+    output->info = info;
+    
+    return output;
+}
+
+static guint8 *
+get_property (Display *dpy,
+	      RROutput output,
+	      Atom atom,
+	      int *len)
+{
+    unsigned char *prop;
+    int actual_format;
+    unsigned long nitems, bytes_after;
+    Atom actual_type;
+    guint8 *result;
+    
+    XRRGetOutputProperty (dpy, output, atom,
+			  0, 100, False, False,
+			  AnyPropertyType,
+			  &actual_type, &actual_format,
+			  &nitems, &bytes_after, &prop);
+    
+    if (actual_type == XA_INTEGER && actual_format == 8)
+    {
+	result = g_memdup (prop, nitems);
+	if (len)
+	    *len = nitems;
+    }
+    else
+    {
+	result = NULL;
+    }
+
+    XFree (prop);
+
+    return result;
+}
+
+static guint8 *
+read_edid_data (RWOutput *output)
+{
+    Atom edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
+    guint8 *result;
+    int len;
+
+    result = get_property (DISPLAY (output),
+			   output->id, edid_atom, &len);
+
+    if (result)
+    {
+	if (len == 128)
+	    return result;
+	else
+	    g_free (result);
+    }
+
+    return NULL;
+}
+
+static void
+output_initialize (RWOutput *output, XRRScreenResources *res)
+{
+    XRROutputInfo *info = XRRGetOutputInfo (
+	DISPLAY (output), res, output->id);
+    GPtrArray *a;
+    int i;
+    
+    g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
+	
+    if (!info || !output->info)
+    {
+	/* FIXME */
+	return;
+    }
+    
+    output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
+    output->current_crtc = crtc_by_id (output->info, info->crtc);
+    output->width_mm = info->mm_width;
+    output->height_mm = info->mm_height;
+    output->connected = (info->connection == RR_Connected);
+    
+    /* Possible crtcs */
+    a = g_ptr_array_new ();
+    
+    for (i = 0; i < info->ncrtc; ++i)
+    {
+	RWCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
+	
+	if (crtc)
+	    g_ptr_array_add (a, crtc);
+    }
+    g_ptr_array_add (a, NULL);
+    output->possible_crtcs = (RWCrtc **)g_ptr_array_free (a, FALSE);
+    
+    /* Clones */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->nclone; ++i)
+    {
+	RWOutput *rw_output = rw_output_by_id (output->info, info->clones[i]);
+	
+	if (rw_output)
+	    g_ptr_array_add (a, rw_output);
+    }
+    g_ptr_array_add (a, NULL);
+    output->clones = (RWOutput **)g_ptr_array_free (a, FALSE);
+    
+    /* Modes */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->nmode; ++i)
+    {
+	RWMode *mode = mode_by_id (output->info, info->modes[i]);
+	
+	if (mode)
+	    g_ptr_array_add (a, mode);
+    }
+    g_ptr_array_add (a, NULL);
+    output->modes = (RWMode **)g_ptr_array_free (a, FALSE);
+
+    output->n_preferred = info->npreferred;
+    
+    /* Edid data */
+    output->edid_data = read_edid_data (output);
+    
+    XRRFreeOutputInfo (info);
+}
+
+static void
+output_free (RWOutput *output)
+{
+    g_free (output);
+}
+
+guint32
+rw_output_get_id (RWOutput *output)
+{
+    g_assert(output != NULL);
+
+    return output->id;
+}
+
+const guint8 *
+rw_output_get_edid_data (RWOutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+
+    return output->edid_data;
+}
+
+RWOutput *
+rw_screen_get_output_by_name (RWScreen        *screen,
+			      const char      *name)
+{
+    int i;
+
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+
+    for (i = 0; screen->info->outputs[i] != NULL; ++i)
+    {
+	RWOutput *output = screen->info->outputs[i];
+
+	if (strcmp (output->name, name) == 0)
+	    return output;
+    }
+
+    return NULL;
+}
+
+RWCrtc *
+rw_output_get_crtc (RWOutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+
+    return output->current_crtc;
+}
+
+RWMode *
+rw_output_get_current_mode (RWOutput *output)
+{
+    RWCrtc *crtc;
+
+    g_return_val_if_fail (output != NULL, NULL);
+
+    if ((crtc = rw_output_get_crtc (output)))
+	return rw_crtc_get_current_mode (crtc);
+
+    return NULL;
+}
+
+void
+rw_output_get_position (RWOutput        *output,
+			int             *x,
+			int             *y)
+{
+    RWCrtc *crtc;
+
+    g_return_if_fail (output != NULL);
+
+    if ((crtc = rw_output_get_crtc (output)))
+	rw_crtc_get_position (crtc, x, y);
+}
+
+const char *
+rw_output_get_name (RWOutput *output)
+{
+    g_assert (output != NULL);
+    return output->name;
+}
+
+int
+rw_output_get_width_mm (RWOutput *output)
+{
+    g_assert (output != NULL);
+    return output->width_mm;
+}
+
+int
+rw_output_get_height_mm (RWOutput *output)
+{
+    g_assert (output != NULL);
+    return output->height_mm;
+}
+
+RWMode *
+rw_output_get_preferred_mode (RWOutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    if (output->n_preferred)
+	return output->modes[0];
+
+    return NULL;
+}
+
+RWMode **
+rw_output_list_modes (RWOutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    return output->modes;
+}
+
+gboolean
+rw_output_is_connected (RWOutput *output)
+{
+    g_return_val_if_fail (output != NULL, FALSE);
+    return output->connected;
+}
+
+gboolean
+rw_output_supports_mode (RWOutput *output,
+			 RWMode   *mode)
+{
+    int i;
+
+    g_return_val_if_fail (output != NULL, FALSE);
+    g_return_val_if_fail (mode != NULL, FALSE);
+
+    for (i = 0; output->modes[i] != NULL; ++i)
+    {
+	if (output->modes[i] == mode)
+	    return TRUE;
+    }
+
+    return FALSE;
+}
+
+gboolean
+rw_output_can_clone (RWOutput *output,
+		     RWOutput *clone)
+{
+    int i;
+
+    g_return_val_if_fail (output != NULL, FALSE);
+    g_return_val_if_fail (clone != NULL, FALSE);
+
+    for (i = 0; output->clones[i] != NULL; ++i)
+    {
+	if (output->clones[i] == clone)
+	    return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* RWCrtc */
+typedef struct
+{
+    Rotation xrot;
+    RWRotation rot;
+} RotationMap;
+static const RotationMap rotation_map[] =
+{
+    { RR_Rotate_0, RW_ROTATION_0 },
+    { RR_Rotate_90, RW_ROTATION_90 },
+    { RR_Rotate_180, RW_ROTATION_180 },
+    { RR_Rotate_270, RW_ROTATION_270 },
+    { RR_Reflect_X, RW_REFLECT_X },
+    { RR_Reflect_Y, RW_REFLECT_Y },
+};
+
+static RWRotation
+rw_rotation_from_xrotation (Rotation r)
+{
+    int i;
+    RWRotation result = 0;
+
+    for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
+    {
+	if (r & rotation_map[i].xrot)
+	    result |= rotation_map[i].rot;
+    }
+
+    return result;
+}
+
+static Rotation
+xrotation_from_rotation (RWRotation r)
+{
+    int i;
+    Rotation result = 0;
+
+    for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
+    {
+	if (r & rotation_map[i].rot)
+	    result |= rotation_map[i].xrot;
+    }
+
+    return result;
+}
+
+gboolean
+rw_crtc_set_config (RWCrtc    *crtc,
+		    int        x,
+		    int        y,
+		    RWMode    *mode,
+		    RWRotation rotation,
+		    RWOutput **outputs,
+		    int        n_outputs)
+{
+    ScreenInfo *info;
+    GArray *output_ids;
+    int i;
+
+    g_return_val_if_fail (crtc != NULL, FALSE);
+    g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
+
+    info = crtc->info;
+    
+    if (mode)
+    {
+	g_return_val_if_fail (x + mode->width <= info->max_width, FALSE);
+	g_return_val_if_fail (y + mode->height <= info->max_height, FALSE);
+    }
+
+    output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput));
+
+    if (outputs)
+    {
+	for (i = 0; i < n_outputs; ++i)
+	    g_array_append_val (output_ids, outputs[i]->id);
+    }
+    
+    XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
+		      CurrentTime, 
+		      x, y,
+		      mode? mode->id : None,
+		      xrotation_from_rotation (rotation),
+		      (RROutput *)output_ids->data,
+		      output_ids->len);
+
+    g_array_free (output_ids, TRUE);
+    
+    return TRUE;
+}
+
+RWMode *
+rw_crtc_get_current_mode (RWCrtc *crtc)
+{
+    g_return_val_if_fail (crtc != NULL, NULL);
+
+    return crtc->current_mode;
+}
+
+guint32
+rw_crtc_get_id (RWCrtc *crtc)
+{
+    g_return_val_if_fail (crtc != NULL, 0);
+
+    return crtc->id;
+}
+
+gboolean
+rw_crtc_can_drive_output (RWCrtc   *crtc,
+			  RWOutput *output)
+{
+    int i;
+
+    g_return_val_if_fail (crtc != NULL, FALSE);
+    g_return_val_if_fail (output != NULL, FALSE);
+    
+    for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
+    {
+	if (crtc->possible_outputs[i] == output)
+	    return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* FIXME: merge with get_mode()? */
+void
+rw_crtc_get_position (RWCrtc          *crtc,
+		      int             *x,
+		      int             *y)
+{
+    g_return_if_fail (crtc != NULL);
+    
+    if (x)
+	*x = crtc->x;
+
+    if (y)
+	*y = crtc->y;
+}
+
+/* FIXME: merge with get_mode()? */
+RWRotation
+rw_crtc_get_current_rotation (RWCrtc *crtc)
+{
+    g_assert(crtc != NULL);
+    return crtc->current_rotation;
+}
+
+RWRotation
+rw_crtc_get_rotations (RWCrtc *crtc)
+{
+    g_assert(crtc != NULL);
+    return crtc->rotations;
+}
+
+gboolean
+rw_crtc_supports_rotation (RWCrtc *   crtc,
+			   RWRotation rotation)
+{
+    g_return_val_if_fail (crtc != NULL, FALSE);
+    return (crtc->rotations & rotation);
+}
+
+static RWCrtc *
+crtc_new (ScreenInfo *info, RROutput id)
+{
+    RWCrtc *crtc = g_new0 (RWCrtc, 1);
+
+    crtc->id = id;
+    crtc->info = info;
+    
+    return crtc;
+}
+
+static void
+crtc_initialize (RWCrtc *crtc, XRRScreenResources *res)
+{
+    XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
+    GPtrArray *a;
+    int i;
+    
+    g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
+	
+    if (!info)
+    {
+	/* FIXME: We need to reaquire the screen resources */
+	return;
+    }
+    
+    /* RWMode */
+    crtc->current_mode = mode_by_id (crtc->info, info->mode);
+    
+    crtc->x = info->x;
+    crtc->y = info->y;
+    
+    /* Current outputs */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->noutput; ++i)
+    {
+	RWOutput *output = rw_output_by_id (crtc->info, info->outputs[i]);
+	
+	if (output)
+	    g_ptr_array_add (a, output);
+    }
+    g_ptr_array_add (a, NULL);
+    crtc->current_outputs = (RWOutput **)g_ptr_array_free (a, FALSE);
+    
+    /* Possible outputs */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->npossible; ++i)
+    {
+	RWOutput *output = rw_output_by_id (crtc->info, info->possible[i]);
+	
+	if (output)
+	    g_ptr_array_add (a, output);
+    }
+    g_ptr_array_add (a, NULL);
+    crtc->possible_outputs = (RWOutput **)g_ptr_array_free (a, FALSE);
+
+    /* Rotations */
+    crtc->current_rotation = rw_rotation_from_xrotation (info->rotation);
+    crtc->rotations = rw_rotation_from_xrotation (info->rotations);
+
+    XRRFreeCrtcInfo (info);
+}
+
+static void
+crtc_free (RWCrtc *crtc)
+{
+    g_free (crtc->current_outputs);
+    g_free (crtc->possible_outputs);
+    g_free (crtc);
+}
+
+/* RWMode */
+static RWMode *
+mode_new (ScreenInfo *info, RRMode id)
+{
+    RWMode *mode = g_new0 (RWMode, 1);
+    
+    mode->id = id;
+    mode->info = info;
+    
+    return mode;
+}
+
+guint32
+rw_mode_get_id (RWMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->id;
+}
+
+guint
+rw_mode_get_width (RWMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->width;
+}
+
+int
+rw_mode_get_freq (RWMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return (mode->freq) / 1000;
+}
+
+guint
+rw_mode_get_height (RWMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->height;
+}
+
+static void
+mode_initialize (RWMode *mode, XRRModeInfo *info)
+{
+    g_assert (mode != NULL);
+    g_assert (info != NULL);
+
+    mode->name = g_strdup (info->name);
+    mode->width = info->width;
+    mode->height = info->height;
+    mode->freq = ((info->dotClock / (double)info->hTotal) / info->vTotal + 0.5) * 1000;
+}
+
+static void
+mode_free (RWMode *mode)
+{
+    g_free (mode->name);
+    g_free (mode);
+}
+
+
+#ifdef INCLUDE_MAIN
+static void
+on_screen_changed (RWScreen *screen, gpointer data)
+{
+    g_print ("Changed\n");
+}
+
+static gboolean
+do_refresh (gpointer data)
+{
+    RWScreen *screen = data;
+
+    rw_screen_refresh (screen);
+
+    return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+    int i;
+    
+    gtk_init (&argc, &argv);
+    
+    RWScreen *screen = rw_screen_new (gdk_screen_get_default(),
+				   on_screen_changed,
+				   NULL);
+    
+    for (i = 0; screen->info->crtcs[i]; ++i)
+    {
+	RWCrtc *crtc = screen->info->crtcs[i];
+	
+	if (crtc->current_mode)
+	{
+	    g_print ("CRTC %p: (%d %d %d %d)\n",
+		     crtc, crtc->x, crtc->y,
+		     crtc->current_mode->width, crtc->current_mode->height);
+	}
+	else
+	{
+	    g_print ("CRTC %p: turned off\n", crtc);
+	}
+    }
+    
+    for (i = 0; screen->info->outputs[i]; ++i)
+    {
+	RWOutput *output = screen->info->outputs[i];
+	
+	g_print ("Output %s currently", output->name);
+	
+	if (!output->current_crtc)
+	    g_print (" turned off\n");
+	else
+	    g_print (" driven by CRTC %p\n", output->current_crtc);
+    }
+
+    g_timeout_add (500, do_refresh, screen);
+    
+    gtk_main ();
+    
+    return 0;
+}
+#endif