gnome-desktop r5116 - in branches/randr-12: . desktop-docs gnome-about libgnome-desktop libgnome-desktop/libgnomeui po



Author: ssp
Date: Mon Jun 16 20:37:26 2008
New Revision: 5116
URL: http://svn.gnome.org/viewvc/gnome-desktop?rev=5116&view=rev

Log:
Revert 'merge'

Modified:
   branches/randr-12/ChangeLog
   branches/randr-12/NEWS
   branches/randr-12/README
   branches/randr-12/configure.in
   branches/randr-12/desktop-docs/ChangeLog
   branches/randr-12/gnome-about/ChangeLog
   branches/randr-12/libgnome-desktop/ChangeLog
   branches/randr-12/libgnome-desktop/Makefile.am
   branches/randr-12/libgnome-desktop/display-name.c
   branches/randr-12/libgnome-desktop/edid-parse.c
   branches/randr-12/libgnome-desktop/edid.h
   branches/randr-12/libgnome-desktop/gnome-bg.c
   branches/randr-12/libgnome-desktop/gnome-rr-config.c
   branches/randr-12/libgnome-desktop/gnome-rr.c
   branches/randr-12/libgnome-desktop/libgnomeui/Makefile.am
   branches/randr-12/libgnome-desktop/libgnomeui/gnome-bg.h
   branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr-config.h
   branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr.h
   branches/randr-12/po/ChangeLog
   branches/randr-12/po/ar.po
   branches/randr-12/po/gu.po

Modified: branches/randr-12/NEWS
==============================================================================
--- branches/randr-12/NEWS	(original)
+++ branches/randr-12/NEWS	Mon Jun 16 20:37:26 2008
@@ -1,29 +1,4 @@
 ==============
-Version 2.23.3
-==============
-
-  libgnome-desktop
-
-	* GnomeBG: Fix some logic errors wrt to caching of slideshows that may
-	  cause nautilus crashes (Matthias Clasen)
-	* GnomeBG: Support multi-resolution backgrounds (Matthias Clasen)
-	* GnomeBG: Falls back to the default image if the user's background
-	  doesn't exist (Ray Strode)
-	* GnomeBG: Prevent loops caused by setting URI (SÃren Sandmann)
-	* GnomeBG: Fix leak (Kjartan Maraas)
-	* GnomeBG: Get rid of URIs and only use paths (SÃren Sandmann)
-	* GnomeBG: Make sure tiles are at least 24 pixels wide (SÃren Sandmann)
-	* GnomeBG: Some tweaks to the tile scaling heuristic (SÃren Sandmann)
-	* GnomeBG: Small fixes (SÃren Sandmann)
-	* GnomeBG: Add getters for the various properties (SÃren Sandmann)
-	* GnomeBG: Monitor the image file (SÃren Sandmann)
-
-  Translators
-
-	* Khaled Hosny (ar)
-	* Sweta Kothari (gu)
-
-==============
 Version 2.23.2
 ==============
 

Modified: branches/randr-12/README
==============================================================================
--- branches/randr-12/README	(original)
+++ branches/randr-12/README	Mon Jun 16 20:37:26 2008
@@ -1,4 +1,4 @@
-gnome-desktop 2.23.3
+gnome-desktop 2.23.2
 ====================
 
 	This package is free software and is part of the

Modified: branches/randr-12/configure.in
==============================================================================
--- branches/randr-12/configure.in	(original)
+++ branches/randr-12/configure.in	Mon Jun 16 20:37:26 2008
@@ -1,4 +1,4 @@
-AC_INIT([gnome-desktop], [2.23.4],
+AC_INIT([gnome-desktop], [2.23.3],
         [http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-desktop])
 AC_CONFIG_SRCDIR([libgnome-desktop])
 
@@ -19,7 +19,7 @@
 #   change to C+1:0:0
 # - If the interface is the same as the previous version, change to C:R+1:A
 
-LT_VERSION=7:0:0
+LT_VERSION=6:8:4
 AC_SUBST(LT_VERSION)
 
 AM_MAINTAINER_MODE
@@ -35,7 +35,7 @@
 
 GNOME_PLATFORM=2
 GNOME_MINOR=23
-GNOME_MICRO=3
+GNOME_MICRO=2
 GNOME_DISTRIBUTOR="GNOME.Org"
 GNOME_DATE=`date +"%Y-%m-%d"`
 

Modified: branches/randr-12/libgnome-desktop/Makefile.am
==============================================================================
--- branches/randr-12/libgnome-desktop/Makefile.am	(original)
+++ branches/randr-12/libgnome-desktop/Makefile.am	Mon Jun 16 20:37:26 2008
@@ -20,7 +20,12 @@
 	gnome-desktop-item.c	\
 	gnome-ditem-edit.c	\
 	gnome-hint.c		\
-	gnome-bg.c
+	gnome-bg.c		\
+	display-name.c		\
+	gnome-rr.c		\
+	gnome-rr-config.c	\
+	edid-parse.c		\
+	edid.h
 
 libgnome_desktop_2_la_LIBADD =	\
 	$(XLIB_LIBS)		\

Modified: branches/randr-12/libgnome-desktop/display-name.c
==============================================================================
--- branches/randr-12/libgnome-desktop/display-name.c	(original)
+++ branches/randr-12/libgnome-desktop/display-name.c	Mon Jun 16 20:37:26 2008
@@ -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);
+}

Modified: branches/randr-12/libgnome-desktop/edid-parse.c
==============================================================================
--- branches/randr-12/libgnome-desktop/edid-parse.c	(original)
+++ branches/randr-12/libgnome-desktop/edid-parse.c	Mon Jun 16 20:37:26 2008
@@ -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;
+}

Modified: branches/randr-12/libgnome-desktop/edid.h
==============================================================================
--- branches/randr-12/libgnome-desktop/edid.h	(original)
+++ branches/randr-12/libgnome-desktop/edid.h	Mon Jun 16 20:37:26 2008
@@ -0,0 +1,199 @@
+/* edid.h
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Soren Sandmann <sandmann redhat com>
+ */
+
+#ifndef EDID_H
+#define EDID_H
+
+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);
+
+#endif

Modified: branches/randr-12/libgnome-desktop/gnome-bg.c
==============================================================================
--- branches/randr-12/libgnome-desktop/gnome-bg.c	(original)
+++ branches/randr-12/libgnome-desktop/gnome-bg.c	Mon Jun 16 20:37:26 2008
@@ -46,8 +46,8 @@
 #define BG_KEY_DRAW_BACKGROUND    GNOME_BG_KEY_DIR "/draw_background"
 #define BG_KEY_PRIMARY_COLOR      GNOME_BG_KEY_DIR "/primary_color"
 #define BG_KEY_SECONDARY_COLOR    GNOME_BG_KEY_DIR "/secondary_color"
-#define BG_KEY_COLOR_TYPE         GNOME_BG_KEY_DIR "/color_shading_type"
-#define BG_KEY_PICTURE_PLACEMENT  GNOME_BG_KEY_DIR "/picture_options"
+#define BG_KEY_COLOR_SHADING_TYPE GNOME_BG_KEY_DIR "/color_shading_type"
+#define BG_KEY_PICTURE_OPTIONS    GNOME_BG_KEY_DIR "/picture_options"
 #define BG_KEY_PICTURE_OPACITY    GNOME_BG_KEY_DIR "/picture_opacity"
 #define BG_KEY_PICTURE_FILENAME   GNOME_BG_KEY_DIR "/picture_filename"
 
@@ -59,17 +59,8 @@
 	double   duration;		/* in seconds */
 	gboolean fixed;
 
-	GSList  *file1;
-	GSList  *file2;		/* NULL if fixed is TRUE */
-};
-
-typedef struct _FileSize FileSize;
-struct _FileSize
-{
-	gint width;
-	gint height;
-
-	char *file;
+	char *file1;
+	char *file2;		/* NULL if fixed is TRUE */
 };
 
 /* This is the size of the GdkRGB dither matrix, in order to avoid
@@ -86,26 +77,21 @@
 struct _GnomeBG
 {
 	GObject                 parent_instance;
-	char *			filename;
+	char *			uri;
 	GnomeBGPlacement	placement;
 	GnomeBGColorType	color_type;
-	GdkColor		primary;
-	GdkColor		secondary;
+	GdkColor		c1;
+	GdkColor		c2;
 
- 	gint                    last_pixmap_width;
- 	gint                    last_pixmap_height;
-
-	GFileMonitor *		file_monitor;
-
-	guint                   changed_id;
-	
 	/* Cached information, only access through cache accessor functions */
         SlideShow *		slideshow;
-	time_t			file_mtime;
+	time_t			uri_mtime;
 	GdkPixbuf *		pixbuf_cache;
 	int			timeout_id;
 
 	GList *		        file_cache;
+
+	guint                   changed_id;
 };
 
 struct _GnomeBGClass
@@ -151,8 +137,8 @@
 					double      alpha);
 
 /* Thumbnail utilities */
-static GdkPixbuf *create_thumbnail_for_filename (GnomeThumbnailFactory *factory,
-						 const char            *filename);
+static GdkPixbuf *create_thumbnail_for_uri (GnomeThumbnailFactory *factory,
+					    const char            *uri);
 static gboolean   get_thumb_annotations (GdkPixbuf             *thumb,
 					 int                   *orig_width,
 					 int                   *orig_height);
@@ -161,96 +147,66 @@
 static GdkPixbuf *get_pixbuf           (GnomeBG               *bg);
 static void       clear_cache          (GnomeBG               *bg);
 static gboolean   is_different         (GnomeBG               *bg,
-					const char            *filename);
-static time_t     get_mtime            (const char            *filename);
+					const char            *uri);
+static time_t     get_mtime            (const char            *uri);
 static GdkPixbuf *create_img_thumbnail (GnomeBG               *bg,
 					GnomeThumbnailFactory *factory,
 					GdkScreen             *screen,
 					int                    dest_width,
 					int                    dest_height);
 static SlideShow * get_as_slideshow    (GnomeBG               *bg,
-					const char 	      *filename);
+					const char 	      *uri);
 static Slide *     get_current_slide   (SlideShow 	      *show,
 		   			double    	      *alpha);
-static gboolean    slideshow_changes_with_size (SlideShow *show);
 
 static void
-color_from_string (const char *string,
-		   GdkColor   *colorp)
+set_color_from_string (const char *string,
+		       GdkColor   *colorp)
 {
 	/* If all else fails use black */
-	gdk_color_parse ("black", colorp);
-
-	if (!string)
-		return;
-
-	if (!gdk_color_parse (string, colorp))
-		return;
-
-	gdk_rgb_find_color (gdk_rgb_get_colormap(), colorp);
-}
-
-static char *
-color_to_string (const GdkColor *color)
-{
-	return g_strdup_printf ("#%02x%02x%02x",
-				color->red >> 8,
-				color->green >> 8,
-				color->blue >> 8);
+	if (string == NULL || !gdk_color_parse (string, colorp)) {
+		gdk_color_parse ("black", colorp);
+	}
+	gdk_rgb_find_color (gdk_rgb_get_colormap (), colorp);
 }
 
-static GConfEnumStringPair placement_lookup[] = {
-	{ GNOME_BG_PLACEMENT_CENTERED,    "centered" },
-	{ GNOME_BG_PLACEMENT_FILL_SCREEN, "stretched" },
-	{ GNOME_BG_PLACEMENT_SCALED,      "scaled" },
-	{ GNOME_BG_PLACEMENT_ZOOMED,      "zoom" },
-	{ GNOME_BG_PLACEMENT_TILED,       "wallpaper" },
-	{ 0, NULL }
-};
-
-static GConfEnumStringPair color_type_lookup[] = {
-	{ GNOME_BG_COLOR_SOLID,      "solid" },
-	{ GNOME_BG_COLOR_H_GRADIENT, "horizontal-gradient" },
-	{ GNOME_BG_COLOR_V_GRADIENT, "vertical-gradient" },
-	{ 0, NULL }
-};
 
 static void
-color_type_from_string (const char       *string,
-			GnomeBGColorType *color_type)
+set_color_type_from_string (const char       *string,
+			    GnomeBGColorType *color_type)
 {
 	*color_type = GNOME_BG_COLOR_SOLID;
 
-	if (string) {
-		gconf_string_to_enum (color_type_lookup,
-				      string, (int *)color_type);
+	if (string != NULL) {
+		if (!strncmp (string, "vertical-gradient", sizeof ("vertical-gradient"))) {
+                        *color_type = GNOME_BG_COLOR_V_GRADIENT;
+		} else if (!strncmp (string, "horizontal-gradient", sizeof ("horizontal-gradient"))) {
+                        *color_type = GNOME_BG_COLOR_H_GRADIENT;
+		}
 	}
 }
 
-static const char *
-color_type_to_string (GnomeBGColorType color_type)
-{
-	return gconf_enum_to_string (color_type_lookup, color_type);
-}
-
 static void
-placement_from_string (const char       *string,
-		       GnomeBGPlacement *placement)
+set_placement_from_string (const char       *string,
+			   GnomeBGPlacement *placement)
 {
 	*placement = GNOME_BG_PLACEMENT_ZOOMED;
 
-	if (string) {
-		gconf_string_to_enum (placement_lookup,
-				      string, (int *)placement);
+	if (string != NULL) {
+		if (!strncmp (string, "wallpaper", sizeof ("wallpaper"))) {
+			*placement = GNOME_BG_PLACEMENT_TILED;
+		} else if (!strncmp (string, "centered", sizeof ("centered"))) {
+			*placement = GNOME_BG_PLACEMENT_CENTERED;
+		} else if (!strncmp (string, "scaled", sizeof ("scaled"))) {
+			*placement = GNOME_BG_PLACEMENT_SCALED;
+		} else if (!strncmp (string, "stretched", sizeof ("stretched"))) {
+			*placement = GNOME_BG_PLACEMENT_FILL_SCREEN;
+		} else if (!strncmp (string, "zoom", sizeof ("zoom"))) {
+			*placement = GNOME_BG_PLACEMENT_ZOOMED;
+		}
 	}
 }
 
-static const char *
-placement_to_string (GnomeBGPlacement placement)
-{
-	return gconf_enum_to_string (placement_lookup, placement);
-}
-
 static gboolean
 do_changed (GnomeBG *bg)
 {
@@ -279,101 +235,48 @@
 				GConfClient *client)
 {
 	char    *tmp;
-	char    *filename;
+	char    *uri;
 
 	g_return_if_fail (GNOME_IS_BG (bg));
 	g_return_if_fail (client != NULL);
 
 	/* Filename */
-	filename = NULL;
+	uri = NULL;
 	tmp = gconf_client_get_string (client, BG_KEY_PICTURE_FILENAME, NULL);
 	if (tmp != NULL) {
 		if (g_utf8_validate (tmp, -1, NULL) &&
 		    g_file_test (tmp, G_FILE_TEST_EXISTS)) {
-			filename = g_strdup (tmp);
+			uri = g_strdup (tmp);
 		} else {
-			filename = g_filename_from_utf8 (tmp, -1, NULL, NULL, NULL);
-		}
-
-		/* Fall back to default background if filename was set
-		   but no longer exists */
-		if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
-			GConfValue *default_value;
-
-			g_free (filename);
-			filename = NULL;
-
-			default_value =
-				gconf_client_get_default_from_schema (client,
-								      BG_KEY_PICTURE_FILENAME,
-								      NULL);
-			if (default_value != NULL) {
-				filename = g_strdup (gconf_value_get_string (default_value));
-				gconf_value_free (default_value);
-			}
+			uri = g_filename_from_utf8 (tmp, -1, NULL, NULL, NULL);
 		}
 	}
 	g_free (tmp);
 
 	/* Colors */
 	tmp = gconf_client_get_string (client, BG_KEY_PRIMARY_COLOR, NULL);
-	color_from_string (tmp, &bg->primary);
+	set_color_from_string (tmp, &bg->c1);
 	g_free (tmp);
 
 	tmp = gconf_client_get_string (client, BG_KEY_SECONDARY_COLOR, NULL);
-	color_from_string (tmp, &bg->secondary);
+	set_color_from_string (tmp, &bg->c2);
 	g_free (tmp);
 
 	/* Color type */
-	tmp = gconf_client_get_string (client, BG_KEY_COLOR_TYPE, NULL);
-	color_type_from_string (tmp, &bg->color_type);
+	tmp = gconf_client_get_string (client, BG_KEY_COLOR_SHADING_TYPE, NULL);
+	set_color_type_from_string (tmp, &bg->color_type);
 	g_free (tmp);
 
 	/* Placement */
-	tmp = gconf_client_get_string (client, BG_KEY_PICTURE_PLACEMENT, NULL);
-	placement_from_string (tmp, &bg->placement);
+	tmp = gconf_client_get_string (client, BG_KEY_PICTURE_OPTIONS, NULL);
+	set_placement_from_string (tmp, &bg->placement);
 	g_free (tmp);
 
-	gnome_bg_set_filename (bg, filename);
+	gnome_bg_set_uri (bg, uri);
 
 	queue_changed (bg);
 }
 
-void
-gnome_bg_save_to_preferences (GnomeBG     *bg,
-			      GConfClient *client)
-{
-	const char *color_type;
-	const char *placement;
-        const gchar *filename;
-        gchar *primary;
-        gchar *secondary;
-        
-	primary = color_to_string (&bg->primary);
-	secondary = color_to_string (&bg->secondary);
-
-	color_type = color_type_to_string (bg->color_type);
-
-        if (bg->filename) {
-		filename = bg->filename;
-		placement = placement_to_string (bg->placement);
-        }
-        else {
-                filename = "(none)";
-                placement = "none";
-        }
-
-	gconf_client_set_string (client, BG_KEY_PICTURE_FILENAME, filename, NULL);
-        gconf_client_set_string (client, BG_KEY_PRIMARY_COLOR, primary, NULL);
-        gconf_client_set_string (client, BG_KEY_SECONDARY_COLOR, secondary, NULL);
-        gconf_client_set_string (client, BG_KEY_COLOR_TYPE, color_type, NULL);
-	gconf_client_set_string (client, BG_KEY_PICTURE_PLACEMENT, placement, NULL);
-
-	g_free (primary);
-	g_free (secondary);
-}
-
-
 static void
 gnome_bg_init (GnomeBG *bg)
 {
@@ -417,29 +320,29 @@
 }
 
 static gboolean
-colors_equal (const GdkColor *primary, const GdkColor *secondary)
+colors_equal (const GdkColor *c1, const GdkColor *c2)
 {
-	return  primary->red   == secondary->red	&&
-		primary->green == secondary->green  &&
-		primary->blue  == secondary->blue;
+	return  c1->red   == c2->red	&&
+		c1->green == c2->green  &&
+		c1->blue  == c2->blue;
 }
 
 void
 gnome_bg_set_color (GnomeBG *bg,
 		    GnomeBGColorType type,
-		    GdkColor *primary,
-		    GdkColor *secondary)
+		    GdkColor *c1,
+		    GdkColor *c2)
 {
 	g_return_if_fail (bg != NULL);
 
 	if (bg->color_type != type			||
-	    !colors_equal (&bg->primary, primary)			||
-	    (secondary && !colors_equal (&bg->secondary, secondary))) {
+	    !colors_equal (&bg->c1, c1)			||
+	    (c2 && !colors_equal (&bg->c2, c2))) {
 
 		bg->color_type = type;
-		bg->primary = *primary;
-		if (secondary) {
-			bg->secondary = *secondary;
+		bg->c1 = *c1;
+		if (c2) {
+			bg->c2 = *c2;
 		}
 
 		queue_changed (bg);
@@ -459,83 +362,26 @@
 	}
 }
 
-GnomeBGPlacement
-gnome_bg_get_placement (GnomeBG *bg)
-{
-	g_return_val_if_fail (bg != NULL, -1);
-
-	return bg->placement;
-}
-
-void
-gnome_bg_get_color (GnomeBG               *bg,
-		    GnomeBGColorType      *type,
-		    GdkColor              *primary,
-		    GdkColor              *secondary)
-{
-	g_return_if_fail (bg != NULL);
-
-	if (type)
-		*type = bg->color_type;
-
-	if (primary)
-		*primary = bg->primary;
-
-	if (secondary)
-		*secondary = bg->secondary;
-}
-
-const gchar *
-gnome_bg_get_filename (GnomeBG *bg)
-{
-	g_return_val_if_fail (bg != NULL, NULL);
-
-	return bg->filename;
-}
-
-static void
-file_changed (GFileMonitor *file_monitor,
-	      GFile *child,
-	      GFile *other_file,
-	      GFileMonitorEvent event_type,
-	      gpointer user_data)
-{
-	GnomeBG *bg = GNOME_BG (user_data);
-
-	clear_cache (bg);
-	queue_changed (bg);
-}
-
 void
-gnome_bg_set_filename (GnomeBG     *bg,
-		       const char  *filename)
+gnome_bg_set_uri (GnomeBG     *bg,
+		  const char  *uri)
 {
 	char *free_me = NULL;
 	
 	g_return_if_fail (bg != NULL);
 	
-	if (is_different (bg, filename)) {
-		char *tmp = g_strdup (filename);
+	if (g_path_is_absolute (uri)) {
+		uri = free_me = g_filename_to_uri (uri, NULL, NULL);
+	}
+	
+	if (is_different (bg, uri)) {
+		char *tmp = g_strdup (uri);
 		
-		g_free (bg->filename);
+		/* FIXME: maybe check that it is local */
 		
-		bg->filename = tmp;
-		bg->file_mtime = get_mtime (bg->filename);
-
-		if (bg->file_monitor) {
-			g_object_unref (bg->file_monitor);
-			bg->file_monitor = NULL;
-		}
-
-		if (bg->filename) {
-			GFile *f = g_file_new_for_path (bg->filename);
-			
-			bg->file_monitor = g_file_monitor_file (f, 0, NULL, NULL);
-			g_signal_connect (bg->file_monitor, "changed",
-					  G_CALLBACK (file_changed), bg);
-
-			g_object_unref (f);
-		}
+		g_free (bg->uri);
+		
+		bg->uri = tmp;
 		
 		clear_cache (bg);
 		
@@ -553,20 +399,20 @@
 	switch (bg->color_type)
 	{
 	case GNOME_BG_COLOR_SOLID:
-		pixel = ((bg->primary.red >> 8) << 24)      |
-			((bg->primary.green >> 8) << 16)    |
-			((bg->primary.blue >> 8) << 8)      |
+		pixel = ((bg->c1.red >> 8) << 24)      |
+			((bg->c1.green >> 8) << 16)    |
+			((bg->c1.blue >> 8) << 8)      |
 			(0xff);
 		
 		gdk_pixbuf_fill (dest, pixel);
 		break;
 		
 	case GNOME_BG_COLOR_H_GRADIENT:
-		pixbuf_draw_gradient (dest, TRUE, &(bg->primary), &(bg->secondary));
+		pixbuf_draw_gradient (dest, TRUE, &(bg->c1), &(bg->c2));
 		break;
 		
 	case GNOME_BG_COLOR_V_GRADIENT:
-		pixbuf_draw_gradient (dest, FALSE, &(bg->primary), &(bg->secondary));
+		pixbuf_draw_gradient (dest, FALSE, &(bg->c1), &(bg->c2));
 		break;
 		
 	default:
@@ -632,7 +478,7 @@
 	
 	scaled = get_scaled_pixbuf (
 		placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
-
+	
 	switch (placement) {
 	case GNOME_BG_PLACEMENT_TILED:
 		pixbuf_tile (scaled, dest);
@@ -666,10 +512,6 @@
 gnome_bg_changes_with_size (GnomeBG *bg)
 {
 	g_return_val_if_fail (bg != NULL, FALSE);
-
-	SlideShow *show = get_as_slideshow (bg, bg->filename);
-	if (show) 
-		return slideshow_changes_with_size (show);
 	
 	if (bg->color_type != GNOME_BG_COLOR_SOLID) {
 		if (!get_pixbuf (bg))
@@ -780,17 +622,7 @@
 	
 	g_return_val_if_fail (bg != NULL, NULL);
 	g_return_val_if_fail (window != NULL, NULL);
-
-	if (bg->last_pixmap_width != width ||
-	    bg->last_pixmap_height != height)  {
-		if (bg->pixbuf_cache) {
-			g_object_unref (bg->pixbuf_cache);
-			bg->pixbuf_cache = NULL;
-		}
-	}
-	bg->last_pixmap_width = width;
-	bg->last_pixmap_height = height;
-
+	
 	gnome_bg_get_pixmap_size (bg, width, height, &pm_width, &pm_height);
 	
 	if (root) {
@@ -803,7 +635,7 @@
 	
 	if (!get_pixbuf (bg) && bg->color_type == GNOME_BG_COLOR_SOLID) {
 		GdkGC *gc = gdk_gc_new (pixmap);
-		gdk_gc_set_rgb_fg_color (gc, &(bg->primary));
+		gdk_gc_set_rgb_fg_color (gc, &(bg->c1));
 		
 		gdk_draw_point (pixmap, gc, 0, 0);
 		
@@ -838,11 +670,11 @@
 	g_return_val_if_fail (bg != NULL, FALSE);
 	
 	if (bg->color_type == GNOME_BG_COLOR_SOLID) {
-		color = bg->primary;
+		color = bg->c1;
 	} else {
-		color.red = (bg->primary.red + bg->secondary.red) / 2;
-		color.green = (bg->primary.green + bg->secondary.green) / 2;
-		color.blue = (bg->primary.blue + bg->secondary.blue) / 2;
+		color.red = (bg->c1.red + bg->c2.red) / 2;
+		color.green = (bg->c1.green + bg->c2.green) / 2;
+		color.blue = (bg->c1.blue + bg->c2.blue) / 2;
 	}
 	
 	if (get_pixbuf (bg)) {
@@ -914,16 +746,24 @@
 }
 
 static gboolean
-get_original_size (const char *filename,
+get_original_size (const char *uri,
 		   int        *orig_width,
 		   int        *orig_height)
 {
+	char *filename;
 	gboolean result;
 
+	if (g_str_has_prefix (uri, "file:"))
+		filename = g_filename_from_uri (uri, NULL, NULL);
+	else 
+		filename = g_strdup (uri);
+	
         if (gdk_pixbuf_get_file_info (filename, orig_width, orig_height))
 		result = TRUE;
 	else
 		result = FALSE;
+	
+	g_free (filename);
 
 	return result;
 }
@@ -936,26 +776,24 @@
 {
 	GdkPixbuf *thumb;
 	gboolean result = FALSE;
-	const gchar *filename;
+	const gchar *uri;
 	
 	g_return_val_if_fail (bg != NULL, FALSE);
 	g_return_val_if_fail (factory != NULL, FALSE);
 	
-	if (!bg->filename)
+	if (!bg->uri)
 		return FALSE;
 	
-	filename = bg->filename;
-	thumb = create_thumbnail_for_filename (factory, filename);
+	uri = bg->uri;
+	thumb = create_thumbnail_for_uri (factory, uri);
 	
 	if (!thumb) {
-		SlideShow *show = get_as_slideshow (bg, bg->filename);
+		SlideShow *show = get_as_slideshow (bg, bg->uri);
 		if (show) {
 			double alpha;
-			FileSize *fs;
 			Slide *slide = get_current_slide (show, &alpha);
-			fs = slide->file1->data;
-			filename = fs->file;
-			thumb = create_thumbnail_for_filename (factory, filename);
+			uri = slide->file1;
+			thumb = create_thumbnail_for_uri (factory, uri);
 		}
 	}
 
@@ -967,7 +805,7 @@
 	}
 
 	if (!result) {
-		if (get_original_size (filename, width, height))
+		if (get_original_size (uri, width, height))
 			result = TRUE;
 	}
 
@@ -1080,14 +918,11 @@
 /* Implementation of the pixbuf cache */
 struct _SlideShow
 {
-	gint ref_count;
 	double start_time;
 	double total_duration;
 
 	GQueue *slides;
 	
-	gboolean changes_with_size;
-
 	/* used during parsing */
 	struct tm start_tm;
 	GQueue *stack;
@@ -1095,8 +930,7 @@
 
 static SlideShow *read_slideshow_file (const char *filename,
 				       GError     **err);
-static void       slideshow_ref       (SlideShow  *show);
-static void       slideshow_unref     (SlideShow  *show);
+static void       slideshow_free      (SlideShow  *show);
 
 static double
 now (void)
@@ -1149,16 +983,14 @@
 	GdkPixbuf *tmp;
 
 	if (gdk_pixbuf_get_width (p2) != gdk_pixbuf_get_width (p1) ||
-            gdk_pixbuf_get_height (p2) != gdk_pixbuf_get_height (p1)) {
-		tmp = gdk_pixbuf_scale_simple (p2, 
-					       gdk_pixbuf_get_width (p1),
-					       gdk_pixbuf_get_height (p1),
-					       GDK_INTERP_BILINEAR);
-	}
-        else {
-		tmp = g_object_ref (p2);
-	}
-	
+            gdk_pixbuf_get_height (p2) != gdk_pixbuf_get_height (p1))
+          tmp = gdk_pixbuf_scale_simple (p2, 
+                                         gdk_pixbuf_get_width (p1),
+                                         gdk_pixbuf_get_height (p1),
+                                         GDK_INTERP_BILINEAR);
+        else
+          tmp = g_object_ref (p2);
+
 	pixbuf_blend (tmp, result, 0, 0, -1, -1, 0, 0, alpha);
         
         g_object_unref (tmp);	
@@ -1175,7 +1007,7 @@
 struct FileCacheEntry
 {
 	FileType type;
-	char *filename;
+	char *uri;
 	union {
 		GdkPixbuf *pixbuf;
 		SlideShow *slideshow;
@@ -1186,14 +1018,14 @@
 static void
 file_cache_entry_delete (FileCacheEntry *ent)
 {
-	g_free (ent->filename);
+	g_free (ent->uri);
 	
 	switch (ent->type) {
 	case PIXBUF:
 		g_object_unref (ent->u.pixbuf);
 		break;
 	case SLIDESHOW:
-		slideshow_unref (ent->u.slideshow);
+		slideshow_free (ent->u.slideshow);
 		break;
 	case THUMBNAIL:
 		g_object_unref (ent->u.thumbnail);
@@ -1217,17 +1049,15 @@
 }
 
 static const FileCacheEntry *
-file_cache_lookup (GnomeBG *bg, FileType type, const char *filename)
+file_cache_lookup (GnomeBG *bg, FileType type, const char *uri)
 {
 	GList *list;
 
 	for (list = bg->file_cache; list != NULL; list = list->next) {
 		FileCacheEntry *ent = list->data;
 
-		if (ent && ent->type == type &&
-		    strcmp (ent->filename, filename) == 0) {
+		if (ent && ent->type == type && strcmp (ent->uri, uri) == 0)
 			return ent;
-		}
 	}
 
 	return NULL;
@@ -1236,14 +1066,14 @@
 static FileCacheEntry *
 file_cache_entry_new (GnomeBG *bg,
 		      FileType type,
-		      const char *filename)
+		      const char *uri)
 {
 	FileCacheEntry *ent = g_new0 (FileCacheEntry, 1);
 
-	g_assert (!file_cache_lookup (bg, type, filename));
+	g_assert (!file_cache_lookup (bg, type, uri));
 	
 	ent->type = type;
-	ent->filename = g_strdup (filename);
+	ent->uri = g_strdup (uri);
 
 	bg->file_cache = g_list_prepend (bg->file_cache, ent);
 
@@ -1254,77 +1084,77 @@
 
 static void
 file_cache_add_pixbuf (GnomeBG *bg,
-		       const char *filename,
+		       const char *uri,
 		       GdkPixbuf *pixbuf)
 {
-	FileCacheEntry *ent = file_cache_entry_new (bg, PIXBUF, filename);
+	FileCacheEntry *ent = file_cache_entry_new (bg, PIXBUF, uri);
 	ent->u.pixbuf = pixbuf;
 }
 
 static void
 file_cache_add_thumbnail (GnomeBG *bg,
-			  const char *filename,
+			  const char *uri,
 			  GdkPixbuf *pixbuf)
 {
-	FileCacheEntry *ent = file_cache_entry_new (bg, THUMBNAIL, filename);
+	FileCacheEntry *ent = file_cache_entry_new (bg, THUMBNAIL, uri);
 	ent->u.thumbnail = pixbuf;
 }
 
 static void
 file_cache_add_slide_show (GnomeBG *bg,
-			   const char *filename,
+			   const char *uri,
 			   SlideShow *show)
 {
-	FileCacheEntry *ent = file_cache_entry_new (bg, SLIDESHOW, filename);
+	FileCacheEntry *ent = file_cache_entry_new (bg, SLIDESHOW, uri);
 	ent->u.slideshow = show;
 }
 
 static GdkPixbuf *
-get_as_pixbuf (GnomeBG *bg, const char *filename)
+get_as_pixbuf (GnomeBG *bg, const char *uri)
 {
 	const FileCacheEntry *ent;
-	if ((ent = file_cache_lookup (bg, PIXBUF, filename))) {
+	if ((ent = file_cache_lookup (bg, PIXBUF, uri))) {
 		return ent->u.pixbuf;
 	}
 	else {
-		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+		GdkPixbuf *pixbuf = gnome_gdk_pixbuf_new_from_uri (uri);
 
 		if (pixbuf)
-			file_cache_add_pixbuf (bg, filename, pixbuf);
+			file_cache_add_pixbuf (bg, uri, pixbuf);
 
 		return pixbuf;
 	}
 }
 
 static SlideShow *
-get_as_slideshow (GnomeBG *bg, const char *filename)
+get_as_slideshow (GnomeBG *bg, const char *uri)
 {
 	const FileCacheEntry *ent;
-	if ((ent = file_cache_lookup (bg, SLIDESHOW, filename))) {
+	if ((ent = file_cache_lookup (bg, SLIDESHOW, uri))) {
 		return ent->u.slideshow;
 	}
 	else {
-		SlideShow *show = read_slideshow_file (filename, NULL);
+		SlideShow *show = read_slideshow_file (uri, NULL);
 
 		if (show)
-			file_cache_add_slide_show (bg, filename, show);
+			file_cache_add_slide_show (bg, uri, show);
 
 		return show;
 	}
 }
 
 static GdkPixbuf *
-get_as_thumbnail (GnomeBG *bg, GnomeThumbnailFactory *factory, const char *filename)
+get_as_thumbnail (GnomeBG *bg, GnomeThumbnailFactory *factory, const char *uri)
 {
 	const FileCacheEntry *ent;
-	if ((ent = file_cache_lookup (bg, THUMBNAIL, filename))) {
+	if ((ent = file_cache_lookup (bg, THUMBNAIL, uri))) {
 		return ent->u.thumbnail;
 	}
 	else {
-		GdkPixbuf *thumb = create_thumbnail_for_filename (factory, filename);
+		GdkPixbuf *thumb = create_thumbnail_for_uri (factory, uri);
 
 		if (thumb)
-			file_cache_add_thumbnail (bg, filename, thumb);
+			file_cache_add_thumbnail (bg, uri, thumb);
 
 		return thumb;
 	}
@@ -1370,7 +1200,7 @@
 }
 
 static time_t
-get_mtime (const char *filename)
+get_mtime (const char *uri)
 {
 	GFile     *file;
 	GFileInfo *info;
@@ -1378,8 +1208,8 @@
 	
 	mtime = (time_t)-1;
 	
-	if (filename) {
-		file = g_file_new_for_path (filename);
+	if (uri) {
+		file = g_file_new_for_uri (uri);
 		info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
 					  G_FILE_QUERY_INFO_NONE, NULL, NULL);
 		if (info) {
@@ -1395,7 +1225,7 @@
 
 static GdkPixbuf *
 scale_thumbnail (GnomeBGPlacement placement,
-		 const char *filename,
+		 const char *uri,
 		 GdkPixbuf *thumb,
 		 GdkScreen *screen,
 		 int	    dest_width,
@@ -1413,8 +1243,13 @@
 		return g_object_ref (thumb);
 	}
 	
+	/* FIXME: in case of tiled, we should probably scale the pixbuf to
+	 * be maybe dest_width/4 pixels tall. While strictly speaking incorrect,
+	 * it might give a better idea of what the background would actually look
+	 * like.
+	 */
 	if (get_thumb_annotations (thumb, &o_width, &o_height)		||
-	    (filename && get_original_size (filename, &o_width, &o_height))) {
+	    (uri && get_original_size (uri, &o_width, &o_height))) {
 		
 		int scr_height = gdk_screen_get_height (screen);
 		int scr_width = gdk_screen_get_width (screen);
@@ -1429,23 +1264,7 @@
 		
 		new_width = floor (thumb_width * f + 0.5);
 		new_height = floor (thumb_height * f + 0.5);
-
-		if (placement == GNOME_BG_PLACEMENT_TILED) {
-			/* Heuristic to make sure tiles don't become so small that
-			 * they turn into a blur.
-			 *
-			 * This is strictly speaking incorrect, but the resulting
-			 * thumbnail gives a much better idea what the background
-			 * will actually look like.
-			 */
-			
-			if ((new_width < 32 || new_height < 32) &&
-			    (new_width < o_width / 4 || new_height < o_height / 4)) {
-				new_width = o_width / 4;
-				new_height = o_height / 4;
-			}
-		}
-			
+		
 		thumb = gdk_pixbuf_scale_simple (thumb, new_width, new_height,
 						 GDK_INTERP_BILINEAR);
 	}
@@ -1462,57 +1281,42 @@
 		      int                    dest_width,
 		      int                    dest_height)
 {
-	if (bg->filename) {
-		GdkPixbuf *thumb = get_as_thumbnail (bg, factory, bg->filename);
+	if (bg->uri) {
+		GdkPixbuf *thumb = get_as_thumbnail (bg, factory, bg->uri);
 
 		if (thumb) {
 			return scale_thumbnail (
-				bg->placement, bg->filename,
+				bg->placement, bg->uri,
 				thumb, screen, dest_width, dest_height);
 		}
 		else {
-			SlideShow *show = get_as_slideshow (bg, bg->filename);
+			SlideShow *show = get_as_slideshow (bg, bg->uri);
 			
 			if (show) {
 				double alpha;
-				Slide *slide;
-
-				slideshow_ref (show);
-
-				slide = get_current_slide (show, &alpha);
+				Slide *slide = get_current_slide (show, &alpha);
 
 				if (slide->fixed) {
 					GdkPixbuf *tmp;
-					FileSize *fs;
 					
-					fs = slide->file1->data;
-					tmp = get_as_thumbnail (bg, factory, fs->file);
+					tmp = get_as_thumbnail (bg, factory, slide->file1);
 
 					thumb = scale_thumbnail (
-						bg->placement, fs->file,
+						bg->placement, bg->uri,
 						tmp, screen, dest_width, dest_height);
 				}
 				else {
-					FileSize *fs;
-					GdkPixbuf *p1, *p2;
-
-					fs = slide->file1->data;
-					p1 = get_as_thumbnail (bg, factory, fs->file);
-
-					fs = slide->file2->data;
-					p2 = get_as_thumbnail (bg, factory, fs->file);
+					GdkPixbuf *p1 = get_as_thumbnail (bg, factory, slide->file1);
+					GdkPixbuf *p2 = get_as_thumbnail (bg, factory, slide->file2);
 
 					if (p1 && p2) {
 						GdkPixbuf *thumb1, *thumb2;
 
-						fs = slide->file1->data;
 						thumb1 = scale_thumbnail (
-							bg->placement, fs->file,
+							bg->placement, bg->uri,
 							p1, screen, dest_width, dest_height);
-
-						fs = slide->file2->data;
 						thumb2 = scale_thumbnail (
-							bg->placement, fs->file,
+							bg->placement, bg->uri,
 							p2, screen, dest_width, dest_height);
 
 						thumb = blend (thumb1, thumb2, alpha);
@@ -1523,8 +1327,6 @@
 				}
 
 				ensure_timeout (bg, slide);
-
-				slideshow_unref (show);
 			}
 		}
 
@@ -1534,51 +1336,6 @@
 	return NULL;
 }
 
-/*
- * Find the FileSize that best matches the given size.
- * Do two passes; the first pass only considers FileSizes
- * that are larger than the given size.
- * We are looking for the image that best matches the aspect ratio.
- * When two images have the same aspect ratio, prefer the one whose
- * width is closer to the given width.
- */
-static FileSize *
-find_best_size (GSList *sizes, gint width, gint height)
-{
-	GSList *s;
-	gdouble a, d, distance;
-	FileSize *best = NULL;
-	gint pass;
-
-	a = width/(gdouble)height;
-	distance = 10000.0;
-
-	for (pass = 0; pass < 2; pass++) {
-		for (s = sizes; s; s = s->next) {
-			FileSize *size = s->data;
-
-			if (pass == 0 && (size->width < width || size->height < height))
-				continue;       
-
-			d = fabs (a - size->width/(gdouble)size->height);
-			if (d < distance) {
-				distance = d;
-				best = size;
-			} 
-			else if (d == distance) {
-				if (abs (size->width - width) < abs (best->width - width)) {
-					best = size;
-				}
-			}
-		}
-
-		if (best)
-			break;
-	}
-
-	return best;
-}
-
 static GdkPixbuf *
 get_pixbuf (GnomeBG *bg)
 {
@@ -1586,35 +1343,24 @@
 	
 	gboolean ref = FALSE;
 	
-	if (!bg->pixbuf_cache && bg->filename) {
+	if (!bg->pixbuf_cache && bg->uri) {
 		ref = TRUE;
-		bg->file_mtime = get_mtime (bg->filename);
+		bg->uri_mtime = get_mtime (bg->uri);
 		
-		bg->pixbuf_cache = get_as_pixbuf (bg, bg->filename);
+		bg->pixbuf_cache = get_as_pixbuf (bg, bg->uri);
 		if (!bg->pixbuf_cache) {
-			SlideShow *show = get_as_slideshow (bg, bg->filename);
+			SlideShow *show = get_as_slideshow (bg, bg->uri);
 
 			if (show) {
 				double alpha;
-				Slide *slide;
-
-				slideshow_ref (show);
-
-				slide = get_current_slide (show, &alpha);
+				Slide *slide = get_current_slide (show, &alpha);
 
 				if (slide->fixed) {
-					FileSize *size;
-					size = find_best_size (slide->file1, bg->last_pixmap_width, bg->last_pixmap_height);
-					bg->pixbuf_cache = get_as_pixbuf (bg, size->file);
+					bg->pixbuf_cache = get_as_pixbuf (bg, slide->file1);
 				}
 				else {
-					FileSize *size;
-					GdkPixbuf *p1, *p2;
-					size = find_best_size (slide->file1, bg->last_pixmap_width, bg->last_pixmap_height);
-					p1 = get_as_pixbuf (bg, size->file);
-					size = find_best_size (slide->file2, bg->last_pixmap_width, bg->last_pixmap_height);
-					p2 = get_as_pixbuf (bg, size->file);
-
+					GdkPixbuf *p1 = get_as_pixbuf (bg, slide->file1);
+					GdkPixbuf *p2 = get_as_pixbuf (bg, slide->file2);
 
 					if (p1 && p2) {
 						bg->pixbuf_cache = blend (p1, p2, alpha);
@@ -1623,8 +1369,6 @@
 				}
 
 				ensure_timeout (bg, slide);
-
-				slideshow_unref (show);
 			}
 		}
 	}
@@ -1637,24 +1381,24 @@
 
 static gboolean
 is_different (GnomeBG    *bg,
-	      const char *filename)
+	      const char *uri)
 {
-	if (!filename && bg->filename) {
+	if (!uri && bg->uri) {
 		return TRUE;
 	}
-	else if (filename && !bg->filename) {
+	else if (uri && !bg->uri) {
 		return TRUE;
 	}
-	else if (!filename && !bg->filename) {
+	else if (!uri && !bg->uri) {
 		return FALSE;
 	}
 	else {
-		time_t mtime = get_mtime (filename);
+		time_t mtime = get_mtime (uri);
 		
-		if (mtime != bg->file_mtime)
+		if (mtime != bg->uri_mtime)
 			return TRUE;
 		
-		if (strcmp (filename, bg->filename) != 0)
+		if (strcmp (uri, bg->uri) != 0)
 			return TRUE;
 		
 		return FALSE;
@@ -1789,8 +1533,8 @@
 }
 
 static guchar *
-create_gradient (const GdkColor *primary,
-		 const GdkColor *secondary,
+create_gradient (const GdkColor *c1,
+		 const GdkColor *c2,
 		 int	          n_pixels)
 {
 	guchar *result = g_malloc (n_pixels * 3);
@@ -1799,9 +1543,9 @@
 	for (i = 0; i < n_pixels; ++i) {
 		double ratio = (i + 0.5) / n_pixels;
 		
-		result[3 * i + 0] = ((guint16) (primary->red * (1 - ratio) + secondary->red * ratio)) >> 8;
-		result[3 * i + 1] = ((guint16) (primary->green * (1 - ratio) + secondary->green * ratio)) >> 8;
-		result[3 * i + 2] = ((guint16) (primary->blue * (1 - ratio) + secondary->blue * ratio)) >> 8;
+		result[3 * i + 0] = ((guint16) (c1->red * (1 - ratio) + c2->red * ratio)) >> 8;
+		result[3 * i + 1] = ((guint16) (c1->green * (1 - ratio) + c2->green * ratio)) >> 8;
+		result[3 * i + 2] = ((guint16) (c1->blue * (1 - ratio) + c2->blue * ratio)) >> 8;
 	}
 	
 	return result;
@@ -1810,8 +1554,8 @@
 static void
 pixbuf_draw_gradient (GdkPixbuf *pixbuf,
 		      gboolean   horizontal,
-		      GdkColor  *primary,
-		      GdkColor  *secondary)
+		      GdkColor  *c1,
+		      GdkColor  *c2)
 {
 	int width  = gdk_pixbuf_get_width (pixbuf);
 	int height = gdk_pixbuf_get_height (pixbuf);
@@ -1820,7 +1564,7 @@
 	guchar *dst_limit = dst + height * rowstride;
 	
 	if (horizontal) {
-		guchar *gradient = create_gradient (primary, secondary, width);
+		guchar *gradient = create_gradient (c1, c2, width);
 		int copy_bytes_per_row = width * 3;
 		
 		while (dst < dst_limit) {
@@ -1831,7 +1575,7 @@
 	} else {
 		guchar *gb, *gradient;
 		
-		gb = gradient = create_gradient (primary, secondary, height);
+		gb = gradient = create_gradient (c1, c2, height);
 		while (dst < dst_limit) {
 			int i;
 			guchar *d = dst;
@@ -1912,7 +1656,6 @@
 	}
 }
 
-static gboolean stack_is (SlideShow *parser, const char *s1, ...);
 
 /* Parser for fading background */
 static void
@@ -1924,7 +1667,6 @@
 		      GError             **err)
 {
 	SlideShow *parser = user_data;
-	gint i;
 	
 	if (strcmp (name, "static") == 0 || strcmp (name, "transition") == 0) {
 		Slide *slide = g_new0 (Slide, 1);
@@ -1934,25 +1676,7 @@
 		
 		g_queue_push_tail (parser->slides, slide);
 	}
-	else if (strcmp (name, "size") == 0) {
-		Slide *slide = parser->slides->tail->data;
-		FileSize *size = g_new0 (FileSize, 1);
-		for (i = 0; attr_names[i]; i++) {
-			if (strcmp (attr_names[i], "width") == 0)
-				size->width = atoi (attr_values[i]);
-			else if (strcmp (attr_names[i], "height") == 0)
-				size->height = atoi (attr_values[i]);
-		}
-		if (parser->stack->tail &&
-		    (strcmp (parser->stack->tail->data, "file") == 0 ||
-		     strcmp (parser->stack->tail->data, "from") == 0)) {
-			slide->file1 = g_slist_prepend (slide->file1, size);
-		}
-		else if (parser->stack->tail &&
-			 strcmp (parser->stack->tail->data, "to") == 0) { 
-			slide->file2 = g_slist_prepend (slide->file2, size);
-		}
-	}
+	
 	g_queue_push_tail (parser->stack, g_strdup (name));
 }
 
@@ -2011,6 +1735,21 @@
 	return strtol (text, NULL, 0);
 }
 
+static char *
+make_uri (char *file)
+{
+	if (g_path_is_absolute (file)) {
+		char *result = g_filename_to_uri (file, NULL, NULL);
+
+		g_free (file);
+
+		return result;
+	}
+	else {
+		return file;
+	}
+}
+
 static void
 handle_text (GMarkupParseContext *context,
 	     const gchar         *text,
@@ -2020,8 +1759,6 @@
 {
 	SlideShow *parser = user_data;
 	Slide *slide = parser->slides->tail? parser->slides->tail->data : NULL;
-	FileSize *fs;
-	gint i;
 
 	if (stack_is (parser, "year", "starttime", "background", NULL)) {
 		parser->start_tm.tm_year = parse_int (text) - 1900;
@@ -2048,87 +1785,28 @@
 	}
 	else if (stack_is (parser, "file", "static", "background", NULL) ||
 		 stack_is (parser, "from", "transition", "background", NULL)) {
-		for (i = 0; text[i]; i++) {
-			if (!g_ascii_isspace (text[i]))
-				break;
-		}
-		if (text[i] == 0)
-			return;
-		fs = g_new (FileSize, 1);
-		fs->width = -1;
-		fs->height = -1;
-		fs->file = g_strdup (text);
-		slide->file1 = g_slist_prepend (slide->file1, fs);
-		if (slide->file1->next != NULL)
-			parser->changes_with_size = TRUE;                       
-	}
-	else if (stack_is (parser, "size", "file", "static", "background", NULL) ||
-		 stack_is (parser, "size", "from", "transition", "background", NULL)) {
-		fs = slide->file1->data;
-		fs->file = g_strdup (text);
-		if (slide->file1->next != NULL)
-			parser->changes_with_size = TRUE; 
+		slide->file1 = g_strdup (text);
+		slide->file1 = make_uri (slide->file1);
 	}
 	else if (stack_is (parser, "to", "transition", "background", NULL)) {
-		for (i = 0; text[i]; i++) {
-			if (!g_ascii_isspace (text[i]))
-				break;
-		}
-		if (text[i] == 0)
-			return;
-		fs = g_new (FileSize, 1);
-		fs->width = -1;
-		fs->height = -1;
-		fs->file = g_strdup (text);
-		slide->file2 = g_slist_prepend (slide->file2, fs);
-		if (slide->file2->next != NULL)
-			parser->changes_with_size = TRUE;                       
+		slide->file2 = g_strdup (text);
+		slide->file2 = make_uri (slide->file2);
 	}
-	else if (stack_is (parser, "size", "to", "transition", "background", NULL)) {
-		fs = slide->file2->data;
-		fs->file = g_strdup (text);
-		if (slide->file2->next != NULL)
-			parser->changes_with_size = TRUE;
-	}
-}
-
-static void
-slideshow_ref (SlideShow *show)
-{
-	show->ref_count++;
 }
 
 static void
-slideshow_unref (SlideShow *show)
+slideshow_free (SlideShow *show)
 {
 	GList *list;
-	GSList *slist;
-	FileSize *size;
-
-	show->ref_count--;
-	if (show->ref_count > 0)
-		return;
-
+	
 	for (list = show->slides->head; list != NULL; list = list->next) {
 		Slide *slide = list->data;
-
-		for (slist = slide->file1; slist != NULL; slist = slist->next) {
-			size = slist->data;
-			g_free (size->file);
-			g_free (size);
-		}
-		g_slist_free (slide->file1);
-
-		for (slist = slide->file2; slist != NULL; slist = slist->next) {
-			size = slist->data;
-			g_free (size->file);
-			g_free (size);
-		}
-		g_slist_free (slide->file2);
-
+		
+		g_free (slide->file1);
+		g_free (slide->file2);
 		g_free (slide);
 	}
-
+	
 	g_queue_free (show->slides);
 	
 	for (list = show->stack->head; list != NULL; list = list->next) {
@@ -2147,26 +1825,15 @@
 {
 #if 0
 	GList *list;
-	GSList *slist;
 	
 	for (list = show->slides->head; list != NULL; list = list->next)
 	{
 		Slide *slide = list->data;
 		
 		g_print ("\nSlide: %s\n", slide->fixed? "fixed" : "transition");
-		g_print ("duration: %f\n", slide->duration);
-		g_print ("File1:\n");
-		for (slist = slide->file1; slist != NULL; slist = slist->next) {
-			FileSize *size = slist->data;
-			g_print ("\t%s (%dx%d)\n", 
-				 size->file, size->width, size->height);
-		}
-		g_print ("File2:\n");
-		for (slist = slide->file2; slist != NULL; slist = slist->next) {
-			FileSize *size = slist->data;
-			g_print ("\t%s (%dx%d)\n", 
-				 size->file, size->width, size->height);
-		}
+		g_print ("duration: %d\n", slide->duration);
+		g_print ("file1: %p %s\n", slide, slide->file1);
+		g_print ("file2: %s\n", slide->file2);
 	}
 #endif
 }
@@ -2189,7 +1856,7 @@
 }
 
 static SlideShow *
-read_slideshow_file (const char *filename,
+read_slideshow_file (const char *uri,
 		     GError     **err)
 {
 	GMarkupParser parser = {
@@ -2207,10 +1874,10 @@
 	GMarkupParseContext *context = NULL;
 	time_t t;
 
-	if (!filename)
+	if (!uri)
 		return NULL;
 
-	file = g_file_new_for_path (filename);
+	file = g_file_new_for_uri (uri);
 	if (!g_file_load_contents (file, NULL, &contents, &len, NULL, NULL)) {
 		g_object_unref (file);
 		return NULL;
@@ -2218,7 +1885,6 @@
 	g_object_unref (file);
 	
 	show = g_new0 (SlideShow, 1);
-	show->ref_count = 1;
 	threadsafe_localtime ((time_t)0, &show->start_tm);
 	show->stack = g_queue_new ();
 	show->slides = g_queue_new ();
@@ -2226,14 +1892,13 @@
 	context = g_markup_parse_context_new (&parser, 0, show, NULL);
 	
 	if (!g_markup_parse_context_parse (context, contents, len, err)) {
-		slideshow_unref (show);
+		slideshow_free (show);
 		show = NULL;
 	}
 	
-
-	if (show) {
-		if (!g_markup_parse_context_end_parse (context, err)) {
-			slideshow_unref (show);
+	if (!g_markup_parse_context_end_parse (context, err)) {
+		if (show) {
+			slideshow_free (show);
 			show = NULL;
 		}
 	}
@@ -2255,52 +1920,47 @@
 
 /* Thumbnail utilities */
 static GdkPixbuf *
-create_thumbnail_for_filename (GnomeThumbnailFactory *factory,
-			       const char            *filename)
+create_thumbnail_for_uri (GnomeThumbnailFactory *factory,
+			  const char            *uri)
 {
-	char *thumb;
+	char *filename;
 	time_t mtime;
-	GdkPixbuf *orig, *result = NULL;
-	char *uri;
+	GdkPixbuf *pixbuf, *orig;
 	
-	mtime = get_mtime (filename);
+	mtime = get_mtime (uri);
 	
 	if (mtime == (time_t)-1)
 		return NULL;
 	
-	uri = g_filename_to_uri (filename, NULL, NULL);
+	filename = gnome_thumbnail_factory_lookup (factory, uri, mtime);
 	
-	thumb = gnome_thumbnail_factory_lookup (factory, uri, mtime);
-	
-	if (thumb) {
-		result = gdk_pixbuf_new_from_file (thumb, NULL);
-		g_free (thumb);
+	if (filename) {
+		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+		
+		return pixbuf;
 	}
-	else {
-		orig = gdk_pixbuf_new_from_file (filename, NULL);
-		if (orig) {
-			int orig_width = gdk_pixbuf_get_width (orig);
-			int orig_height = gdk_pixbuf_get_height (orig);
-			
-			result = pixbuf_scale_to_fit (orig, 128, 128);
-			
-			g_object_set_data_full (G_OBJECT (result), "gnome-thumbnail-height",
-						g_strdup_printf ("%d", orig_height), g_free);
-			g_object_set_data_full (G_OBJECT (result), "gnome-thumbnail-width",
-						g_strdup_printf ("%d", orig_width), g_free);
-			
-			g_object_unref (orig);
-			
-			gnome_thumbnail_factory_save_thumbnail (factory, result, uri, mtime);
-		}
-		else {
-			gnome_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
-		}
+	
+	orig = gnome_gdk_pixbuf_new_from_uri (uri);
+	if (orig) {
+		int orig_width = gdk_pixbuf_get_width (orig);
+		int orig_height = gdk_pixbuf_get_height (orig);
+		
+		pixbuf = pixbuf_scale_to_fit (orig, 128, 128);
+		
+		g_object_set_data_full (G_OBJECT (pixbuf), "gnome-thumbnail-height",
+					g_strdup_printf ("%d", orig_height), g_free);
+		g_object_set_data_full (G_OBJECT (pixbuf), "gnome-thumbnail-width",
+					g_strdup_printf ("%d", orig_width), g_free);
+		
+		g_object_unref (orig);
+		
+		gnome_thumbnail_factory_save_thumbnail (factory, pixbuf, uri, mtime);
+		
+		return pixbuf;
 	}
-
-	g_free (uri);
-
-	return result;
+	
+	gnome_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
+	return NULL;
 }
 
 static gboolean
@@ -2328,10 +1988,3 @@
 	
 	return FALSE;
 }
-
-static gboolean
-slideshow_changes_with_size (SlideShow *show)
-{
-	return show->changes_with_size;
-}
-

Modified: branches/randr-12/libgnome-desktop/gnome-rr-config.c
==============================================================================
--- branches/randr-12/libgnome-desktop/gnome-rr-config.c	(original)
+++ branches/randr-12/libgnome-desktop/gnome-rr-config.c	Mon Jun 16 20:37:26 2008
@@ -0,0 +1,1407 @@
+/* gnome-rr-config.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Soren Sandmann <sandmann redhat com>
+ */
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "libgnomeui/gnome-rr-config.h"
+#include "edid.h"
+
+#define CONFIG_BASENAME "monitors.xml"
+
+/* In version 0 of the config file format, we had several <configuration> toplevel elements
+ * and no explicit version number.  So, the filed looked like
+ *
+ *   <configuration>
+ *     ...
+ *   </configuration>
+ *   <configuration>
+ *     ...
+ *   </configuration>
+ *
+ * Since version 1 of the config file, the file has a toplevel <monitors> element to group
+ * all the configurations.  That element has a "version" attribute which is an integer.
+ * So, the file looks like this:
+ *
+ *   <monitors version="1">
+ *     <configuration>
+ *       ...
+ *     </configuration>
+ *     <configuration>
+ *       ...
+ *     </configuration>
+ *   </monitors>
+ */
+
+/* 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   (GnomeRRScreen    *screen,
+					       GnomeOutputInfo **outputs);
+static void             crtc_assignment_free  (CrtcAssignment   *assign);
+static void             output_free           (GnomeOutputInfo  *output);
+static GnomeOutputInfo *output_copy           (GnomeOutputInfo  *output);
+
+static gchar *get_old_config_filename (void);
+static gchar *get_config_filename (void);
+
+typedef struct Parser Parser;
+
+/* Parser for monitor configurations */
+struct Parser
+{
+    int			config_file_version;
+    GnomeOutputInfo *	output;
+    GnomeRRConfig *	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 (GnomeOutputInfo, 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 (GnomeRRConfig, 1);
+	parser->configuration->clone = FALSE;
+	parser->configuration->outputs = g_new0 (GnomeOutputInfo *, 1);
+    }
+    else if (strcmp (name, "monitors") == 0)
+    {
+	int i;
+
+	for (i = 0; attr_names[i] != NULL; i++)
+	{
+	    if (strcmp (attr_names[i], "version") == 0)
+	    {
+		parser->config_file_version = parse_int (attr_values[i]);
+		break;
+	    }
+	}
+    }
+
+    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 GNOME_RR_ROTATION_0 */
+	if (parser->output->rotation == 0)
+	    parser->output->rotation = GNOME_RR_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 =
+	    (GnomeOutputInfo **)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));
+}
+
+#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
+
+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", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->connected = TRUE;
+	
+	strncpy (parser->output->vendor, text, 3);
+	parser->output->vendor[3] = 0;
+    }
+    else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	    parser->configuration->clone = TRUE;
+    }
+    else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->connected = TRUE;
+
+	parser->output->product = parse_int (text);
+    }
+    else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->connected = TRUE;
+
+	parser->output->serial = parse_uint (text);
+    }
+    else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->width = parse_int (text);
+    }
+    else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->x = parse_int (text);
+    }
+    else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->y = parse_int (text);
+    }
+    else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->height = parse_int (text);
+    }
+    else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	parser->output->on = TRUE;
+
+	parser->output->rate = parse_int (text);
+    }
+    else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	if (strcmp (text, "normal") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_ROTATION_0;
+	}
+	else if (strcmp (text, "left") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_ROTATION_90;
+	}
+	else if (strcmp (text, "upside_down") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_ROTATION_180;
+	}
+	else if (strcmp (text, "right") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_ROTATION_270;
+	}
+    }
+    else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_REFLECT_X;
+	}
+    }
+    else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	if (strcmp (text, "yes") == 0)
+	{
+	    parser->output->rotation |= GNOME_RR_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)
+	gnome_rr_config_free (parser->configuration);
+
+    for (i = 0; i < parser->outputs->len; ++i)
+    {
+	GnomeOutputInfo *output = parser->outputs->pdata[i];
+
+	output_free (output);
+    }
+
+    g_ptr_array_free (parser->outputs, TRUE);
+
+    for (i = 0; i < parser->configurations->len; ++i)
+    {
+	GnomeRRConfig *config = parser->configurations->pdata[i];
+
+	gnome_rr_config_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 GnomeRRConfig **
+configurations_read_from_file (const gchar *filename, GError **error)
+{
+    Parser *parser = g_new0 (Parser, 1);
+    GnomeRRConfig **result;
+    GMarkupParser callbacks = {
+	handle_start_element,
+	handle_end_element,
+	handle_text,
+	NULL, /* passthrough */
+	NULL, /* error */
+    };
+
+    parser->config_file_version = 0;
+    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 = (GnomeRRConfig **)g_ptr_array_free (parser->configurations, FALSE);
+    parser->configurations = g_ptr_array_new ();
+    
+    g_assert (parser->outputs);
+out:
+    parser_free (parser);
+
+    return result;
+}
+
+static GnomeRRConfig **
+configurations_read (GError **error)
+{
+    char *filename;
+    GnomeRRConfig **configs;
+    GError *err;
+
+    /* Try the new configuration file... */
+
+    filename = get_config_filename ();
+
+    err = NULL;
+    configs = configurations_read_from_file (filename, &err);
+
+    g_free (filename);
+
+    if (g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+    {
+	g_error_free (err);
+
+	/* Okay, so try the old configuration file */
+	filename = get_old_config_filename ();
+	configs = configurations_read_from_file (filename, error);
+	g_free (filename);
+
+	return configs;
+    }
+
+    g_propagate_error (error, err);
+    return configs;
+}
+
+GnomeRRConfig *
+gnome_rr_config_new_current (GnomeRRScreen *screen)
+{
+    GnomeRRConfig *config = g_new0 (GnomeRRConfig, 1);
+    GPtrArray *a = g_ptr_array_new ();
+    GnomeRROutput **rr_outputs;
+    int i;
+    int clone_width = -1;
+    int clone_height = -1;
+
+    g_return_val_if_fail (screen != NULL, NULL);
+
+    rr_outputs = gnome_rr_screen_list_outputs (screen);
+
+    config->clone = FALSE;
+    
+    for (i = 0; rr_outputs[i] != NULL; ++i)
+    {
+	GnomeRROutput *rr_output = rr_outputs[i];
+	GnomeOutputInfo *output = g_new0 (GnomeOutputInfo, 1);
+	GnomeRRMode *mode = NULL;
+	const guint8 *edid_data = gnome_rr_output_get_edid_data (rr_output);
+	GnomeRRCrtc *crtc;
+
+	output->name = g_strdup (gnome_rr_output_get_name (rr_output));
+	output->connected = gnome_rr_output_is_connected (rr_output);
+
+	if (!output->connected)
+	{
+	    output->x = -1;
+	    output->y = -1;
+	    output->width = -1;
+	    output->height = -1;
+	    output->rate = -1;
+	    output->rotation = GNOME_RR_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 (
+		gnome_rr_output_get_name (rr_output), info);
+		
+	    g_free (info);
+		
+	    crtc = gnome_rr_output_get_crtc (rr_output);
+	    mode = crtc? gnome_rr_crtc_get_current_mode (crtc) : NULL;
+	    
+	    if (crtc && mode)
+	    {
+		output->on = TRUE;
+		
+		gnome_rr_crtc_get_position (crtc, &output->x, &output->y);
+		output->width = gnome_rr_mode_get_width (mode);
+		output->height = gnome_rr_mode_get_height (mode);
+		output->rate = gnome_rr_mode_get_freq (mode);
+		output->rotation = gnome_rr_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 = gnome_rr_output_get_preferred_mode (rr_output);
+	    
+	    if (!mode)
+	    {
+		GnomeRRMode **modes = gnome_rr_output_list_modes (rr_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 = gnome_rr_mode_get_width (mode);
+		output->pref_height = gnome_rr_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 = (GnomeOutputInfo **)g_ptr_array_free (a, FALSE);
+
+    g_assert (gnome_rr_config_match (config, config));
+    
+    return config;
+}
+
+static void
+output_free (GnomeOutputInfo *output)
+{
+    if (output->display_name)
+	g_free (output->display_name);
+
+    if (output->name)
+	g_free (output->name);
+    
+    g_free (output);
+}
+
+static GnomeOutputInfo *
+output_copy (GnomeOutputInfo *output)
+{
+    GnomeOutputInfo *copy = g_new0 (GnomeOutputInfo, 1);
+
+    *copy = *output;
+
+    copy->name = g_strdup (output->name);
+    copy->display_name = g_strdup (output->display_name);
+
+    return copy;
+}
+
+static void
+outputs_free (GnomeOutputInfo **outputs)
+{
+    int i;
+
+    g_assert (outputs != NULL);
+
+    for (i = 0; outputs[i] != NULL; ++i)
+	output_free (outputs[i]);
+}
+
+void
+gnome_rr_config_free (GnomeRRConfig *config)
+{
+    g_return_if_fail (config != NULL);
+    outputs_free (config->outputs);
+    
+    g_free (config);
+}
+
+static void
+configurations_free (GnomeRRConfig **configurations)
+{
+    int i;
+
+    g_assert (configurations != NULL);
+
+    for (i = 0; configurations[i] != NULL; ++i)
+	gnome_rr_config_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 (GnomeOutputInfo *output1, GnomeOutputInfo *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 GnomeOutputInfo *
+find_output (GnomeRRConfig *config, const char *name)
+{
+    int i;
+
+    for (i = 0; config->outputs[i] != NULL; ++i)
+    {
+	GnomeOutputInfo *output = config->outputs[i];
+	
+	if (strcmp (name, output->name) == 0)
+	    return output;
+    }
+
+    return NULL;
+}
+
+gboolean
+gnome_rr_config_match (GnomeRRConfig *c1, GnomeRRConfig *c2)
+{
+    int i;
+
+    for (i = 0; c1->outputs[i] != NULL; ++i)
+    {
+	GnomeOutputInfo *output1 = c1->outputs[i];
+	GnomeOutputInfo *output2;
+
+	output2 = find_output (c2, output1->name);
+	if (!output2 || !output_match (output1, output2))
+	    return FALSE;
+    }
+    
+    return TRUE;
+}
+
+static GnomeOutputInfo **
+make_outputs (GnomeRRConfig *config)
+{
+    GPtrArray *outputs;
+    GnomeOutputInfo *first_on;;
+    int i;
+
+    outputs = g_ptr_array_new ();
+
+    first_on = NULL;
+    
+    for (i = 0; config->outputs[i] != NULL; ++i)
+    {
+	GnomeOutputInfo *old = config->outputs[i];
+	GnomeOutputInfo *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 (GnomeOutputInfo **)g_ptr_array_free (outputs, FALSE);
+}
+
+gboolean
+gnome_rr_config_applicable (GnomeRRConfig  *configuration,
+			  GnomeRRScreen       *screen)
+{
+    GnomeOutputInfo **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 GnomeRRConfig *
+gnome_rr_config_find (GnomeRRConfig **haystack,
+		    GnomeRRConfig  *needle)
+{
+    int i;
+
+    for (i = 0; haystack[i] != NULL; ++i)
+    {
+	if (gnome_rr_config_match (haystack[i], needle))
+	    return haystack[i];
+    }
+
+    return NULL;
+}
+
+/* Database management */
+
+static gchar *
+get_old_config_filename (void)
+{
+    return g_build_filename (g_get_home_dir(), ".gnome2", CONFIG_BASENAME, NULL);
+}
+
+static gchar *
+get_config_filename (void)
+{
+    return g_build_filename (g_get_user_config_dir (), CONFIG_BASENAME, NULL);
+}
+
+static const char *
+get_rotation_name (GnomeRRRotation r)
+{
+    if (r & GNOME_RR_ROTATION_0)
+	return "normal";
+    if (r & GNOME_RR_ROTATION_90)
+	return "left";
+    if (r & GNOME_RR_ROTATION_180)
+	return "upside_down";
+    if (r & GNOME_RR_ROTATION_270)
+	return "right";
+
+    return "normal";
+}
+
+static const char *
+yes_no (int x)
+{
+    return x? "yes" : "no";
+}
+
+static const char *
+get_reflect_x (GnomeRRRotation r)
+{
+    return yes_no (r & GNOME_RR_REFLECT_X);
+}
+
+static const char *
+get_reflect_y (GnomeRRRotation r)
+{
+    return yes_no (r & GNOME_RR_REFLECT_Y);
+}
+
+static void
+emit_configuration (GnomeRRConfig *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)
+    {
+	GnomeOutputInfo *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
+gnome_rr_config_sanitize (GnomeRRConfig *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)
+    {
+	GnomeOutputInfo *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)
+    {
+	GnomeOutputInfo *output = config->outputs[i];
+	
+	if (output->on)
+	{
+	    output->x -= x_offset;
+	    output->y -= y_offset;
+	}
+    }
+}
+
+
+gboolean
+gnome_rr_config_save (GnomeRRConfig *configuration, GError **err)
+{
+    GnomeRRConfig **configurations;
+    GString *output = g_string_new("");
+    int i;
+    gchar *filename;
+    gboolean result;
+
+    configurations = configurations_read (NULL); /* NULL-GError */
+    
+    g_string_append_printf (output, "<monitors version=\"1\">\n");
+
+    if (configurations)
+    {
+	for (i = 0; configurations[i] != NULL; ++i)
+	{
+	    if (!gnome_rr_config_match (configurations[i], configuration))
+		emit_configuration (configurations[i], output);
+	}
+
+	configurations_free (configurations);
+    }
+
+    emit_configuration (configuration, output);
+
+    g_string_append_printf (output, "</monitors>\n");
+
+    filename = get_config_filename ();
+    result = g_file_set_contents (filename, output->str, -1, err);
+    g_free (filename);
+
+    if (result)
+    {
+	/* Only remove the old config file if we were successful in saving the new one */
+
+	filename = get_old_config_filename ();
+	if (g_file_test (filename, G_FILE_TEST_EXISTS))
+	    g_unlink (filename);
+
+	g_free (filename);
+    }
+
+    return result;
+}
+
+static gboolean
+apply_configuration (GnomeRRConfig *conf, GnomeRRScreen *screen)
+{
+    CrtcAssignment *assignment;
+    GnomeOutputInfo **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
+gnome_rr_config_apply_stored (GnomeRRScreen *screen)
+{
+    GnomeRRConfig **configs = configurations_read (NULL); /* NULL-GError */
+    GnomeRRConfig *current;
+    GnomeRRConfig *found;
+    gboolean result = TRUE;
+
+    if (!screen)
+	return FALSE;
+    
+    gnome_rr_screen_refresh (screen);
+    
+    current = gnome_rr_config_new_current (screen);
+    if (configs)
+    {
+	if ((found = gnome_rr_config_find (configs, current)))
+	{
+	    apply_configuration (found, screen);
+	    result = TRUE;
+	}
+	else
+	{
+	    result = FALSE;
+	}
+	
+	configurations_free (configs);
+    }
+	
+    gnome_rr_config_free (current);
+
+    return result;
+}
+
+
+/*
+ * CRTC assignment
+ */
+typedef struct CrtcInfo CrtcInfo;
+
+struct CrtcInfo
+{
+    GnomeRRMode    *mode;
+    int        x;
+    int        y;
+    GnomeRRRotation rotation;
+    GPtrArray *outputs;
+};
+
+struct CrtcAssignment
+{
+    GnomeRRScreen *screen;
+    GHashTable *info;
+};
+
+static gboolean
+can_clone (CrtcInfo *info,
+	   GnomeRROutput *output)
+{
+    int i;
+
+    for (i = 0; i < info->outputs->len; ++i)
+    {
+	GnomeRROutput *clone = info->outputs->pdata[i];
+
+	if (!gnome_rr_output_can_clone (clone, output))
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+crtc_assignment_assign (CrtcAssignment *assign,
+			GnomeRRCrtc         *crtc,
+			GnomeRRMode         *mode,
+			int             x,
+			int             y,
+			GnomeRRRotation      rotation,
+			GnomeRROutput       *output)
+{
+    /* FIXME: We should reject stuff that is outside the screen ranges */
+    
+    CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+    if (!gnome_rr_crtc_can_drive_output (crtc, output) ||
+	!gnome_rr_output_supports_mode (output, mode)  ||
+	!gnome_rr_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,
+			  GnomeRRCrtc         *crtc,
+			  GnomeRROutput       *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)
+{
+    GnomeRRCrtc *crtc = key;
+    CrtcInfo *info = value;
+
+    gnome_rr_crtc_set_config (crtc,
+			info->x, info->y,
+			info->mode,
+			info->rotation,
+			(GnomeRROutput **)info->outputs->pdata,
+			info->outputs->len);
+}
+
+static gboolean
+mode_is_rotated (CrtcInfo *info)
+{
+    if ((info->rotation & GNOME_RR_ROTATION_270)		||
+	(info->rotation & GNOME_RR_ROTATION_90))
+    {
+	return TRUE;
+    }
+    return FALSE;
+}
+
+static gboolean
+crtc_is_rotated (GnomeRRCrtc *crtc)
+{
+    GnomeRRRotation r = gnome_rr_crtc_get_current_rotation (crtc);
+
+    if ((r & GNOME_RR_ROTATION_270)		||
+	(r & GNOME_RR_ROTATION_90))
+    {
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+crtc_assignment_apply (CrtcAssignment *assign)
+{
+    GList *active_crtcs = g_hash_table_get_keys (assign->info);
+    GnomeRRCrtc **all_crtcs = gnome_rr_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)
+    {
+	GnomeRRCrtc *crtc = list->data;
+	CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+	int w, h;
+
+	w = gnome_rr_mode_get_width (info->mode);
+	h = gnome_rr_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);
+
+    gnome_rr_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)
+    {
+	GnomeRRCrtc *crtc = all_crtcs[i];
+	GnomeRRMode *mode = gnome_rr_crtc_get_current_mode (crtc);
+	int x, y;
+
+	if (mode)
+	{
+	    int w, h;
+	    gnome_rr_crtc_get_position (crtc, &x, &y);
+
+	    w = gnome_rr_mode_get_width (mode);
+	    h = gnome_rr_mode_get_height (mode);
+	    
+	    if (crtc_is_rotated (crtc))
+	    {
+		int tmp = h;
+		h = w;
+		w = tmp;
+	    }
+	    
+	    if (x + w > width || y + h > height)
+		gnome_rr_crtc_set_config (crtc, 0, 0, NULL, GNOME_RR_ROTATION_0, NULL, 0);
+	}
+    }
+
+    /* Turn off all CRTC's that are not in the assignment */
+    for (i = 0; all_crtcs[i] != NULL; ++i)
+    {
+	GnomeRRCrtc *crtc = all_crtcs[i];
+	
+	if (!g_hash_table_lookup (assign->info, crtc))
+	    gnome_rr_crtc_set_config (crtc, 0, 0, NULL, GNOME_RR_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;
+    
+    gnome_rr_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 (GnomeRRScreen *screen,
+		   GnomeOutputInfo **outputs,
+		   CrtcAssignment *assignment)
+{
+    GnomeRRCrtc **crtcs = gnome_rr_screen_list_crtcs (screen);
+    GnomeOutputInfo *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)
+	{
+	    GnomeRRCrtc *crtc = crtcs[i];
+	    GnomeRROutput *gnome_rr_output =
+		gnome_rr_screen_get_output_by_name (screen, output->name);
+	    GnomeRRMode **modes = gnome_rr_output_list_modes (gnome_rr_output);
+	    int j;
+	
+	    for (j = 0; modes[j] != NULL; ++j)
+	    {
+		GnomeRRMode *mode = modes[j];
+		
+		if (gnome_rr_mode_get_width (mode) == output->width	&&
+		    gnome_rr_mode_get_height (mode) == output->height &&
+		    (pass == 1 || gnome_rr_mode_get_freq (mode) == output->rate))
+		{
+		    if (crtc_assignment_assign (
+			    assignment, crtc, modes[j],
+			    output->x, output->y,
+			    output->rotation,
+			    gnome_rr_output))
+		    {
+			if (real_assign_crtcs (screen, outputs + 1, assignment))
+			    return TRUE;
+			
+			crtc_assignment_unassign (assignment, crtc, gnome_rr_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 (GnomeRRScreen *screen, GnomeOutputInfo **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;
+    }
+}

Modified: branches/randr-12/libgnome-desktop/gnome-rr.c
==============================================================================
--- branches/randr-12/libgnome-desktop/gnome-rr.c	(original)
+++ branches/randr-12/libgnome-desktop/gnome-rr.c	Mon Jun 16 20:37:26 2008
@@ -0,0 +1,1215 @@
+/* gnome-rr.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Soren Sandmann <sandmann redhat com>
+ */
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+
+#include "libgnomeui/gnome-rr.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;
+    
+    GnomeRROutput **	outputs;
+    GnomeRRCrtc **	crtcs;
+    GnomeRRMode **	modes;
+    
+    GnomeRRScreen *	screen;
+};
+
+struct GnomeRRScreen
+{
+    GdkScreen *			gdk_screen;
+    GdkWindow *			gdk_root;
+    Display *			xdisplay;
+    Screen *			xscreen;
+    Window			xroot;
+    ScreenInfo *		info;
+    
+    int				randr_event_base;
+    
+    GnomeRRScreenChanged	callback;
+    gpointer			data;
+};
+
+struct GnomeRROutput
+{
+    ScreenInfo *	info;
+    RROutput		id;
+    
+    char *		name;
+    GnomeRRCrtc *	current_crtc;
+    gboolean		connected;
+    gulong		width_mm;
+    gulong		height_mm;
+    GnomeRRCrtc **	possible_crtcs;
+    GnomeRROutput **	clones;
+    GnomeRRMode **	modes;
+    int			n_preferred;
+    guint8 *		edid_data;
+};
+
+struct GnomeRROutputWrap
+{
+    RROutput		id;
+};
+
+struct GnomeRRCrtc
+{
+    ScreenInfo *	info;
+    RRCrtc		id;
+    
+    GnomeRRMode *	current_mode;
+    GnomeRROutput **	current_outputs;
+    GnomeRROutput **	possible_outputs;
+    int			x;
+    int			y;
+    
+    GnomeRRRotation	current_rotation;
+    GnomeRRRotation	rotations;
+};
+
+struct GnomeRRMode
+{
+    ScreenInfo *	info;
+    RRMode		id;
+    char *		name;
+    int			width;
+    int			height;
+    int			freq;		/* in mHz */
+};
+
+/* GnomeRRCrtc */
+static GnomeRRCrtc *  crtc_new          (ScreenInfo         *info,
+					 RRCrtc              id);
+static void           crtc_free         (GnomeRRCrtc        *crtc);
+static void           crtc_initialize   (GnomeRRCrtc        *crtc,
+					 XRRScreenResources *res);
+
+/* GnomeRROutput */
+static GnomeRROutput *output_new        (ScreenInfo         *info,
+					 RROutput            id);
+static void           output_initialize (GnomeRROutput      *output,
+					 XRRScreenResources *res);
+static void           output_free       (GnomeRROutput      *output);
+
+/* GnomeRRMode */
+static GnomeRRMode *  mode_new          (ScreenInfo         *info,
+					 RRMode              id);
+static void           mode_initialize   (GnomeRRMode        *mode,
+					 XRRModeInfo        *info);
+static void           mode_free         (GnomeRRMode        *mode);
+
+
+/* Screen */
+static GnomeRROutput *
+gnome_rr_output_by_id (ScreenInfo *info, RROutput id)
+{
+    GnomeRROutput **output;
+    
+    g_assert (info != NULL);
+    
+    for (output = info->outputs; *output; ++output)
+    {
+	if ((*output)->id == id)
+	    return *output;
+    }
+    
+    return NULL;
+}
+
+static GnomeRRCrtc *
+crtc_by_id (ScreenInfo *info, RRCrtc id)
+{
+    GnomeRRCrtc **crtc;
+    
+    if (!info)
+        return NULL;
+    
+    for (crtc = info->crtcs; *crtc; ++crtc)
+    {
+	if ((*crtc)->id == id)
+	    return *crtc;
+    }
+    
+    return NULL;
+}
+
+static GnomeRRMode *
+mode_by_id (ScreenInfo *info, RRMode id)
+{
+    GnomeRRMode **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)
+{
+    GnomeRROutput **output;
+    GnomeRRCrtc **crtc;
+    GnomeRRMode **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;
+	GnomeRRCrtc **crtc;
+	GnomeRROutput **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)
+	{
+	    GnomeRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
+	    
+	    g_ptr_array_add (a, crtc);
+	}
+	g_ptr_array_add (a, NULL);
+	info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
+	
+	a = g_ptr_array_new ();
+	for (i = 0; i < resources->noutput; ++i)
+	{
+	    GnomeRROutput *output = output_new (info, resources->outputs[i]);
+	    
+	    g_ptr_array_add (a, output);
+	}
+	g_ptr_array_add (a, NULL);
+	info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+	
+	a = g_ptr_array_new ();
+	for (i = 0;  i < resources->nmode; ++i)
+	{
+	    GnomeRRMode *mode = mode_new (info, resources->modes[i].id);
+	    
+	    g_ptr_array_add (a, mode);
+	}
+	g_ptr_array_add (a, NULL);
+	info->modes = (GnomeRRMode **)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)
+	{
+	    GnomeRRMode *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 (GnomeRRScreen *screen)
+{
+    ScreenInfo *info = g_new0 (ScreenInfo, 1);
+    
+    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;
+    }
+}
+
+static gboolean
+screen_update (GnomeRRScreen *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)
+{
+    GnomeRRScreen *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
+ * the driver does not support Xrandr 1.2.
+ */
+GnomeRRScreen *
+gnome_rr_screen_new (GdkScreen *gdk_screen,
+		     GnomeRRScreenChanged callback,
+		     gpointer data)
+{
+    Display *dpy = GDK_SCREEN_XDISPLAY (gdk_screen);
+    int event_base;
+    int ignore;
+    
+    if (XRRQueryExtension (dpy, &event_base, &ignore))
+    {
+	GnomeRRScreen *screen = g_new0 (GnomeRRScreen, 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
+gnome_rr_screen_set_size (GnomeRRScreen *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
+gnome_rr_screen_get_ranges (GnomeRRScreen *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
+gnome_rr_screen_refresh (GnomeRRScreen *screen)
+{
+    return screen_update (screen, FALSE);
+}
+
+GnomeRRMode **
+gnome_rr_screen_list_modes (GnomeRRScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+    
+    return screen->info->modes;
+}
+
+GnomeRRCrtc **
+gnome_rr_screen_list_crtcs (GnomeRRScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+    
+    return screen->info->crtcs;
+}
+
+GnomeRROutput **
+gnome_rr_screen_list_outputs (GnomeRRScreen *screen)
+{
+    g_return_val_if_fail (screen != NULL, NULL);
+    g_return_val_if_fail (screen->info != NULL, NULL);
+    
+    return screen->info->outputs;
+}
+
+GnomeRRCrtc *
+gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *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;
+}
+
+GnomeRROutput *
+gnome_rr_screen_get_output_by_id (GnomeRRScreen *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;
+}
+
+/* GnomeRROutput */
+static GnomeRROutput *
+output_new (ScreenInfo *info, RROutput id)
+{
+    GnomeRROutput *output = g_new0 (GnomeRROutput, 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 (GnomeRROutput *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 (GnomeRROutput *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)
+    {
+	GnomeRRCrtc *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 = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
+    
+    /* Clones */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->nclone; ++i)
+    {
+	GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, info->clones[i]);
+	
+	if (gnome_rr_output)
+	    g_ptr_array_add (a, gnome_rr_output);
+    }
+    g_ptr_array_add (a, NULL);
+    output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+    
+    /* Modes */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->nmode; ++i)
+    {
+	GnomeRRMode *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 = (GnomeRRMode **)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 (GnomeRROutput *output)
+{
+    g_free (output);
+}
+
+guint32
+gnome_rr_output_get_id (GnomeRROutput *output)
+{
+    g_assert(output != NULL);
+    
+    return output->id;
+}
+
+const guint8 *
+gnome_rr_output_get_edid_data (GnomeRROutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    
+    return output->edid_data;
+}
+
+GnomeRROutput *
+gnome_rr_screen_get_output_by_name (GnomeRRScreen *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)
+    {
+	GnomeRROutput *output = screen->info->outputs[i];
+	
+	if (strcmp (output->name, name) == 0)
+	    return output;
+    }
+    
+    return NULL;
+}
+
+GnomeRRCrtc *
+gnome_rr_output_get_crtc (GnomeRROutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    
+    return output->current_crtc;
+}
+
+GnomeRRMode *
+gnome_rr_output_get_current_mode (GnomeRROutput *output)
+{
+    GnomeRRCrtc *crtc;
+    
+    g_return_val_if_fail (output != NULL, NULL);
+    
+    if ((crtc = gnome_rr_output_get_crtc (output)))
+	return gnome_rr_crtc_get_current_mode (crtc);
+    
+    return NULL;
+}
+
+void
+gnome_rr_output_get_position (GnomeRROutput   *output,
+			      int             *x,
+			      int             *y)
+{
+    GnomeRRCrtc *crtc;
+    
+    g_return_if_fail (output != NULL);
+    
+    if ((crtc = gnome_rr_output_get_crtc (output)))
+	gnome_rr_crtc_get_position (crtc, x, y);
+}
+
+const char *
+gnome_rr_output_get_name (GnomeRROutput *output)
+{
+    g_assert (output != NULL);
+    return output->name;
+}
+
+int
+gnome_rr_output_get_width_mm (GnomeRROutput *output)
+{
+    g_assert (output != NULL);
+    return output->width_mm;
+}
+
+int
+gnome_rr_output_get_height_mm (GnomeRROutput *output)
+{
+    g_assert (output != NULL);
+    return output->height_mm;
+}
+
+GnomeRRMode *
+gnome_rr_output_get_preferred_mode (GnomeRROutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    if (output->n_preferred)
+	return output->modes[0];
+    
+    return NULL;
+}
+
+GnomeRRMode **
+gnome_rr_output_list_modes (GnomeRROutput *output)
+{
+    g_return_val_if_fail (output != NULL, NULL);
+    return output->modes;
+}
+
+gboolean
+gnome_rr_output_is_connected (GnomeRROutput *output)
+{
+    g_return_val_if_fail (output != NULL, FALSE);
+    return output->connected;
+}
+
+gboolean
+gnome_rr_output_supports_mode (GnomeRROutput *output,
+			       GnomeRRMode   *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
+gnome_rr_output_can_clone (GnomeRROutput *output,
+			   GnomeRROutput *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;
+}
+
+/* GnomeRRCrtc */
+typedef struct
+{
+    Rotation xrot;
+    GnomeRRRotation rot;
+} RotationMap;
+
+static const RotationMap rotation_map[] =
+{
+    { RR_Rotate_0, GNOME_RR_ROTATION_0 },
+    { RR_Rotate_90, GNOME_RR_ROTATION_90 },
+    { RR_Rotate_180, GNOME_RR_ROTATION_180 },
+    { RR_Rotate_270, GNOME_RR_ROTATION_270 },
+    { RR_Reflect_X, GNOME_RR_REFLECT_X },
+    { RR_Reflect_Y, GNOME_RR_REFLECT_Y },
+};
+
+static GnomeRRRotation
+gnome_rr_rotation_from_xrotation (Rotation r)
+{
+    int i;
+    GnomeRRRotation 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 (GnomeRRRotation 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
+gnome_rr_crtc_set_config (GnomeRRCrtc      *crtc,
+			  int               x,
+			  int               y,
+			  GnomeRRMode      *mode,
+			  GnomeRRRotation   rotation,
+			  GnomeRROutput   **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;
+}
+
+GnomeRRMode *
+gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc)
+{
+    g_return_val_if_fail (crtc != NULL, NULL);
+    
+    return crtc->current_mode;
+}
+
+guint32
+gnome_rr_crtc_get_id (GnomeRRCrtc *crtc)
+{
+    g_return_val_if_fail (crtc != NULL, 0);
+    
+    return crtc->id;
+}
+
+gboolean
+gnome_rr_crtc_can_drive_output (GnomeRRCrtc   *crtc,
+				GnomeRROutput *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
+gnome_rr_crtc_get_position (GnomeRRCrtc *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()? */
+GnomeRRRotation
+gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc)
+{
+    g_assert(crtc != NULL);
+    return crtc->current_rotation;
+}
+
+GnomeRRRotation
+gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc)
+{
+    g_assert(crtc != NULL);
+    return crtc->rotations;
+}
+
+gboolean
+gnome_rr_crtc_supports_rotation (GnomeRRCrtc *   crtc,
+				 GnomeRRRotation rotation)
+{
+    g_return_val_if_fail (crtc != NULL, FALSE);
+    return (crtc->rotations & rotation);
+}
+
+static GnomeRRCrtc *
+crtc_new (ScreenInfo *info, RROutput id)
+{
+    GnomeRRCrtc *crtc = g_new0 (GnomeRRCrtc, 1);
+    
+    crtc->id = id;
+    crtc->info = info;
+    
+    return crtc;
+}
+
+static void
+crtc_initialize (GnomeRRCrtc        *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;
+    }
+    
+    /* GnomeRRMode */
+    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)
+    {
+	GnomeRROutput *output = gnome_rr_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 = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+    
+    /* Possible outputs */
+    a = g_ptr_array_new ();
+    for (i = 0; i < info->npossible; ++i)
+    {
+	GnomeRROutput *output = gnome_rr_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 = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+    
+    /* Rotations */
+    crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation);
+    crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations);
+    
+    XRRFreeCrtcInfo (info);
+}
+
+static void
+crtc_free (GnomeRRCrtc *crtc)
+{
+    g_free (crtc->current_outputs);
+    g_free (crtc->possible_outputs);
+    g_free (crtc);
+}
+
+/* GnomeRRMode */
+static GnomeRRMode *
+mode_new (ScreenInfo *info, RRMode id)
+{
+    GnomeRRMode *mode = g_new0 (GnomeRRMode, 1);
+    
+    mode->id = id;
+    mode->info = info;
+    
+    return mode;
+}
+
+guint32
+gnome_rr_mode_get_id (GnomeRRMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->id;
+}
+
+guint
+gnome_rr_mode_get_width (GnomeRRMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->width;
+}
+
+int
+gnome_rr_mode_get_freq (GnomeRRMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return (mode->freq) / 1000;
+}
+
+guint
+gnome_rr_mode_get_height (GnomeRRMode *mode)
+{
+    g_return_val_if_fail (mode != NULL, 0);
+    return mode->height;
+}
+
+static void
+mode_initialize (GnomeRRMode *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 (GnomeRRMode *mode)
+{
+    g_free (mode->name);
+    g_free (mode);
+}
+
+
+#ifdef INCLUDE_MAIN
+static void
+on_screen_changed (GnomeRRScreen *screen, gpointer data)
+{
+    g_print ("Changed\n");
+}
+
+static gboolean
+do_refresh (gpointer data)
+{
+    GnomeRRScreen *screen = data;
+    
+    gnome_rr_screen_refresh (screen);
+    
+    return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+    int i;
+    
+    gtk_init (&argc, &argv);
+    
+    GnomeRRScreen *screen = gnome_rr_screen_new (gdk_screen_get_default(),
+					    on_screen_changed,
+					    NULL);
+    
+    for (i = 0; screen->info->crtcs[i]; ++i)
+    {
+	GnomeRRCrtc *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)
+    {
+	GnomeRROutput *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

Modified: branches/randr-12/libgnome-desktop/libgnomeui/Makefile.am
==============================================================================
--- branches/randr-12/libgnome-desktop/libgnomeui/Makefile.am	(original)
+++ branches/randr-12/libgnome-desktop/libgnomeui/Makefile.am	Mon Jun 16 20:37:26 2008
@@ -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		\
+	gnome-rr.h		\
+	gnome-rr-config.h

Modified: branches/randr-12/libgnome-desktop/libgnomeui/gnome-bg.h
==============================================================================
--- branches/randr-12/libgnome-desktop/libgnomeui/gnome-bg.h	(original)
+++ branches/randr-12/libgnome-desktop/libgnomeui/gnome-bg.h	Mon Jun 16 20:37:26 2008
@@ -61,54 +61,43 @@
 	GNOME_BG_PLACEMENT_FILL_SCREEN
 } GnomeBGPlacement;
 
-GType            gnome_bg_get_type              (void);
-GnomeBG *        gnome_bg_new                   (void);
-void             gnome_bg_load_from_preferences (GnomeBG               *bg,
-						 GConfClient           *client);
-void             gnome_bg_save_to_preferences   (GnomeBG               *bg,
-						 GConfClient           *client);
-/* Setters */
-void             gnome_bg_set_filename          (GnomeBG               *bg,
-						 const char            *filename);
-void             gnome_bg_set_placement         (GnomeBG               *bg,
-						 GnomeBGPlacement       placement);
-void             gnome_bg_set_color             (GnomeBG               *bg,
-						 GnomeBGColorType       type,
-						 GdkColor              *primary,
-						 GdkColor              *secondary);
-/* Getters */
-GnomeBGPlacement gnome_bg_get_placement         (GnomeBG               *bg);
-void		 gnome_bg_get_color             (GnomeBG               *bg,
-						 GnomeBGColorType      *type,
-						 GdkColor              *primary,
-						 GdkColor              *secondary);
-const gchar *    gnome_bg_get_filename          (GnomeBG               *bg);
-
-/* Drawing and thumbnailing */
-void             gnome_bg_draw                  (GnomeBG               *bg,
-						 GdkPixbuf             *dest);
-GdkPixmap *      gnome_bg_create_pixmap         (GnomeBG               *bg,
-						 GdkWindow             *window,
-						 int                    width,
-						 int                    height,
-						 gboolean               root);
-gboolean         gnome_bg_get_image_size        (GnomeBG               *bg,
-						 GnomeThumbnailFactory *factory,
-						 int                   *width,
-						 int                   *height);
-GdkPixbuf *      gnome_bg_create_thumbnail      (GnomeBG               *bg,
-						 GnomeThumbnailFactory *factory,
-						 GdkScreen             *screen,
-						 int                    dest_width,
-						 int                    dest_height);
-gboolean         gnome_bg_is_dark               (GnomeBG               *bg);
-gboolean         gnome_bg_changes_with_size     (GnomeBG               *bg);
+GType      gnome_bg_get_type           (void);
+GnomeBG *  gnome_bg_new                (void);
 
+void       gnome_bg_load_from_preferences (GnomeBG               *bg,
+                                           GConfClient           *client);
+
+void       gnome_bg_set_placement      (GnomeBG               *img,
+					GnomeBGPlacement       placement);
+void       gnome_bg_set_color          (GnomeBG               *img,
+					GnomeBGColorType       type,
+					GdkColor              *c1,
+					GdkColor              *c2);
+void       gnome_bg_set_uri            (GnomeBG               *img,
+					const char            *uri);
+void       gnome_bg_draw               (GnomeBG               *img,
+					GdkPixbuf             *dest);
+GdkPixmap *gnome_bg_create_pixmap      (GnomeBG               *img,
+					GdkWindow             *window,
+					int                    width,
+					int                    height,
+					gboolean               root);
+gboolean   gnome_bg_get_image_size     (GnomeBG	               *bg,
+					GnomeThumbnailFactory *factory,
+					int		       *width,
+					int		       *height);
+GdkPixbuf *gnome_bg_create_thumbnail   (GnomeBG               *bg,
+					GnomeThumbnailFactory *factory,
+					GdkScreen             *screen,
+					int		       dest_width,
+					int		       dest_height);
+gboolean   gnome_bg_is_dark            (GnomeBG               *img);
+gboolean   gnome_bg_changes_with_size  (GnomeBG               *img);
 
-/* Set a pixmap as root - not a GnomeBG method */
-void             gnome_bg_set_pixmap_as_root    (GdkScreen             *screen,
-						 GdkPixmap             *pixmap);
 
+/* Set a pixmap as root - not a GnomeBG method */
+void       gnome_bg_set_pixmap_as_root (GdkScreen             *screen,
+					GdkPixmap             *pixmap);
 
 G_END_DECLS
 

Modified: branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr-config.h
==============================================================================
--- branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr-config.h	(original)
+++ branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr-config.h	Mon Jun 16 20:37:26 2008
@@ -0,0 +1,79 @@
+/* gnome-rr-config.h
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Soren Sandmann <sandmann redhat com>
+ */
+#ifndef GNOME_RR_CONFIG_H
+#define GNOME_RR_CONFIG_H
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#error   gnome-rr-config.h is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnome-rr-config.h
+#endif
+
+#include <libgnomeui/gnome-rr.h>
+#include <glib.h>
+
+typedef struct GnomeOutputInfo GnomeOutputInfo;
+typedef struct GnomeRRConfig GnomeRRConfig;
+
+struct GnomeOutputInfo
+{
+    char *		name;
+
+    gboolean		on;
+    int			width;
+    int			height;
+    int			rate;
+    int			x;
+    int			y;
+    GnomeRRRotation	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 GnomeRRConfig
+{
+    gboolean		clone;
+    
+    GnomeOutputInfo **	outputs;
+};
+
+GnomeRRConfig  *gnome_rr_config_new_current  (GnomeRRScreen  *screen);
+void            gnome_rr_config_free         (GnomeRRConfig  *configuration);
+gboolean        gnome_rr_config_match        (GnomeRRConfig  *config1,
+					      GnomeRRConfig  *config2);
+gboolean        gnome_rr_config_save         (GnomeRRConfig  *configuration,
+					      GError        **err);
+void            gnome_rr_config_sanitize     (GnomeRRConfig  *configuration);
+gboolean        gnome_rr_config_apply_stored (GnomeRRScreen  *screen);
+gboolean        gnome_rr_config_applicable   (GnomeRRConfig  *configuration,
+					      GnomeRRScreen  *screen);
+
+#endif

Modified: branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr.h
==============================================================================
--- branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr.h	(original)
+++ branches/randr-12/libgnome-desktop/libgnomeui/gnome-rr.h	Mon Jun 16 20:37:26 2008
@@ -0,0 +1,123 @@
+/* randrwrap.h
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Soren Sandmann <sandmann redhat com>
+ */
+#ifndef RANDR_WRAP_H
+#define RANDR_WRAP_H
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#error    GnomeRR is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnomerr.h
+#endif
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+typedef struct GnomeRRScreen GnomeRRScreen;
+typedef struct GnomeRROutput GnomeRROutput;
+typedef struct GnomeRRCrtc GnomeRRCrtc;
+typedef struct GnomeRRMode GnomeRRMode;
+
+typedef void (* GnomeRRScreenChanged) (GnomeRRScreen *screen, gpointer data);
+
+typedef enum
+{
+    GNOME_RR_ROTATION_0 =	(1 << 0),
+    GNOME_RR_ROTATION_90 =	(1 << 1),
+    GNOME_RR_ROTATION_180 =	(1 << 2),
+    GNOME_RR_ROTATION_270 =	(1 << 3),
+    GNOME_RR_REFLECT_X =	(1 << 4),
+    GNOME_RR_REFLECT_Y =	(1 << 5)
+} GnomeRRRotation;
+
+/* GnomeRRScreen */
+GnomeRRScreen * gnome_rr_screen_new                (GdkScreen             *screen,
+						    GnomeRRScreenChanged   callback,
+						    gpointer               data);
+GnomeRROutput **gnome_rr_screen_list_outputs       (GnomeRRScreen         *screen);
+GnomeRRCrtc **  gnome_rr_screen_list_crtcs         (GnomeRRScreen         *screen);
+GnomeRRMode **  gnome_rr_screen_list_modes         (GnomeRRScreen         *screen);
+void            gnome_rr_screen_set_size           (GnomeRRScreen         *screen,
+						    int                    width,
+						    int                    height,
+						    int                    mm_width,
+						    int                    mm_height);
+GnomeRRCrtc *   gnome_rr_screen_get_crtc_by_id     (GnomeRRScreen         *screen,
+						    guint32                id);
+gboolean        gnome_rr_screen_refresh            (GnomeRRScreen         *screen);
+GnomeRROutput * gnome_rr_screen_get_output_by_id   (GnomeRRScreen         *screen,
+						    guint32                id);
+GnomeRROutput * gnome_rr_screen_get_output_by_name (GnomeRRScreen         *screen,
+						    const char            *name);
+void            gnome_rr_screen_get_ranges         (GnomeRRScreen         *screen,
+						    int                   *min_width,
+						    int                   *max_width,
+						    int                   *min_height,
+						    int                   *max_height);
+
+/* GnomeRROutput */
+guint32         gnome_rr_output_get_id             (GnomeRROutput         *output);
+const char *    gnome_rr_output_get_name           (GnomeRROutput         *output);
+gboolean        gnome_rr_output_is_connected       (GnomeRROutput         *output);
+int             gnome_rr_output_get_size_inches    (GnomeRROutput         *output);
+int             gnome_rr_output_get_width_mm       (GnomeRROutput         *outout);
+int             gnome_rr_output_get_height_mm      (GnomeRROutput         *output);
+const guint8 *  gnome_rr_output_get_edid_data      (GnomeRROutput         *output);
+GnomeRRCrtc **  gnome_rr_output_get_possible_crtcs (GnomeRROutput         *output);
+GnomeRRMode *   gnome_rr_output_get_current_mode   (GnomeRROutput         *output);
+GnomeRRCrtc *   gnome_rr_output_get_crtc           (GnomeRROutput         *output);
+void            gnome_rr_output_get_position       (GnomeRROutput         *output,
+						    int                   *x,
+						    int                   *y);
+gboolean        gnome_rr_output_can_clone          (GnomeRROutput         *output,
+						    GnomeRROutput         *clone);
+GnomeRRMode **  gnome_rr_output_list_modes         (GnomeRROutput         *output);
+GnomeRRMode *   gnome_rr_output_get_preferred_mode (GnomeRROutput         *output);
+gboolean        gnome_rr_output_supports_mode      (GnomeRROutput         *output,
+						    GnomeRRMode           *mode);
+
+/* GnomeRRMode */
+guint32         gnome_rr_mode_get_id               (GnomeRRMode           *mode);
+guint           gnome_rr_mode_get_width            (GnomeRRMode           *mode);
+guint           gnome_rr_mode_get_height           (GnomeRRMode           *mode);
+int             gnome_rr_mode_get_freq             (GnomeRRMode           *mode);
+
+/* GnomeRRCrtc */
+guint32         gnome_rr_crtc_get_id               (GnomeRRCrtc           *crtc);
+gboolean        gnome_rr_crtc_set_config           (GnomeRRCrtc           *crtc,
+						    int                    x,
+						    int                    y,
+						    GnomeRRMode           *mode,
+						    GnomeRRRotation        rotation,
+						    GnomeRROutput        **outputs,
+						    int                    n_outputs);
+gboolean        gnome_rr_crtc_can_drive_output     (GnomeRRCrtc           *crtc,
+						    GnomeRROutput         *output);
+GnomeRRMode *   gnome_rr_crtc_get_current_mode     (GnomeRRCrtc           *crtc);
+void            gnome_rr_crtc_get_position         (GnomeRRCrtc           *crtc,
+						    int                   *x,
+						    int                   *y);
+GnomeRRRotation gnome_rr_crtc_get_current_rotation (GnomeRRCrtc           *crtc);
+GnomeRRRotation gnome_rr_crtc_get_rotations        (GnomeRRCrtc           *crtc);
+gboolean        gnome_rr_crtc_supports_rotation    (GnomeRRCrtc           *crtc,
+						    GnomeRRRotation        rotation);
+
+#endif



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