[Xfce4-commits] [xfce/xfce4-settings] 01/01: Introduction of a new drag-and-drop display settings dialog. This new dialog works more reliably and is significantly easier to use with multiple monitors. It is based on the MATE Display Settings dialog, while maintaining the Xfce flair.

noreply at xfce.org noreply at xfce.org
Thu Jul 10 03:34:42 CEST 2014


This is an automated email from the git hooks/post-receive script.

bluesabre pushed a commit to branch master
in repository xfce/xfce4-settings.

commit 3903d0ab91b3840eccf9c52f4000b4808c13aec6
Author: Sean Davis <smd.seandavis at gmail.com>
Date:   Wed Jul 9 21:28:16 2014 -0400

    Introduction of a new drag-and-drop display settings dialog.
    This new dialog works more reliably and is significantly easier to use with multiple monitors. It is based on the MATE Display Settings dialog, while maintaining the Xfce flair.
    
    Squashed commit of the following:
    
    commit 02a6e2204a5f91aed09b21a0851d53a607674fe4
    Merge: 3518045 0b289d2
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Jul 9 21:25:26 2014 -0400
    
        Merge branch 'master' of github.com:bluesabre/xfce4-settings into display-settings
    
    commit 0b289d2ffba38bff01b1275fc7effd03b055ab5c
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Jul 9 21:04:28 2014 -0400
    
        Do not automatically show popups on screen event
    
    commit b2c34610d8655c9c97cc17520336b41541db739a
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Jul 9 20:56:45 2014 -0400
    
        Drop the icon from the combobox
    
    commit 08caa14626f5541c71688167aae58c4c145e4422
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed Jul 9 20:47:58 2014 -0400
    
        Better numbering
    
    commit 25178c8bef12d54ca3ee13ca4f9fd7f3fb0f3f65
    Author: Simon Steinbeiss <simon.steinbeiss at elfenbeinturm.at>
    Date:   Wed Jul 9 19:56:09 2014 -0400
    
        Fix radial gradient on non-primary displays
    
    commit 3db18859788f1aff7da469797aa7e150db13bf99
    Author: Simon Steinbeiss <simon.steinbeiss at elfenbeinturm.at>
    Date:   Tue Jul 8 02:03:39 2014 +0200
    
        Ellipsize the labels in the display-widgets
    
    commit 3202b8d93704d69f98bd455f1e095f635dd67d5a
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Jul 6 19:42:43 2014 -0400
    
        Store only output ids
    
    commit 1c828cc780b6742e0e19a002392f6f6ae8a337ee
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sun Jul 6 17:06:35 2014 -0400
    
        Fix Display settings mix up monitor EDID names (bug 10717)
    
    commit 97801953fc69349220442631bc3ff0f44eff40cb
    Author: Victor Yap <victor.yap at alumni.concordia.ca>
    Date:   Sun Jul 6 15:21:05 2014 -0400
    
        Add display output numerical identifier prefix
    
    commit cb5c36049a9cb7b1b4ae7d25c8a8c44ad69b47ce
    Author: Simon Steinbeiss <simon.steinbeiss at elfenbeinturm.at>
    Date:   Tue Jun 10 15:46:56 2014 +0200
    
        Create a more complex reflection and improve sharpness
    
    commit b26d92f8aeccebee3038e555bf86075c8da8f63f
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Thu Jun 5 22:02:51 2014 -0400
    
        Reflect resolution, rotation, and mirror settings in gui immediately
    
    commit ccbd17d896856db0cf1c53b2939b1add75ad901c
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Thu Jun 5 21:33:14 2014 -0400
    
        Prevent disabled outputs from overlapping each other.
    
    commit 8843f45c6ea64278f8507a38d5c696a327f45db0
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Thu Jun 5 20:24:16 2014 -0400
    
        Snap displayed edges for better appearance.
    
    commit d05001e8de155dcf73addcbbba07ac94358a9038
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Thu Jun 5 00:11:22 2014 -0400
    
        Drop Position comboboxes, make DND fully operational with addition of Apply button.
    
    commit 55b88cf951970cadcf6fa0a3b49f7f1346f28b41
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Jun 3 21:20:26 2014 -0400
    
        Correctly represent position as coordinates
    
    commit b6cdf5f1a01cffc7b177ae9c94c8e1d7e1f4c3e5
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue Jun 3 21:09:45 2014 -0400
    
        Fix segfault on cairo_pattern_t
    
    commit e8160fddd75ef5bb591c78be0f634bc21d502a60
    Author: Simon Steinbeiss <simon.steinbeiss at elfenbeinturm.at>
    Date:   Tue Jun 3 16:00:30 2014 +0200
    
        Migrate to fixed colors, add more fancy cairo drawing and add a label for the disabled output
        Try to make sure that the outlines are all sharp by rounding
        FIXME: displays are actually overlapping on the border pixels
    
    commit 98f620670ea1eea235a188e4a4e24dca8bd37a2a
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sat May 31 06:37:09 2014 -0400
    
        Use the tooltip to display position
    
    commit 599506a4037b07c9f296d6fe298516e72b80f55a
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Fri May 30 23:50:55 2014 -0400
    
        Cleanup remaining debug strings and mate names
    
    commit 7c99c8b1c48cfaa2c5d88bc970cdea5176a7dc99
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Fri May 30 23:40:49 2014 -0400
    
        Center displays again.
    
    commit bf1766f794eac4fe26eaa24dd6fe79aaebdd26f9
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Fri May 30 23:28:48 2014 -0400
    
        Fix alignment by not zeroing positions until mouse release
    
    commit 1669eab8aaa2f7ab64e8a1f3889ba6907493b641
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Fri May 30 22:47:43 2014 -0400
    
        Improved scaling
    
    commit dd15e1bfa69ea847321181f8cf5f79fb231b099e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Wed May 28 07:06:53 2014 -0400
    
        Change selected display on click in gui
    
    commit 4265acd7d52901d7352322e10baba576ad8cf3e5
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue May 27 22:53:36 2014 -0400
    
        Additional cleanup, fix debug builds.
    
    commit 8f81f62dbe8f779c93b09e165b43926507e19b14
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue May 27 21:37:52 2014 -0400
    
        Consistent spacing.
    
    commit b53ff618f1456babf40f8e59859d937f42d3707e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue May 27 20:39:00 2014 -0400
    
        Further code cleanup
    
    commit 8fcc5e99b2a76751933c6f99083a5afe95757f04
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue May 27 07:26:48 2014 -0400
    
        Fix initial layouts
    
    commit 02c5c8be0b7a726082aec146120bc59ac32e4e08
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Tue May 27 06:35:12 2014 -0400
    
        Functional DND that does not yet commit changes
    
    commit 9b8eede7a90345cea0363306b3bfc1b5c12f5d0e
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Mon May 26 21:27:53 2014 -0400
    
        Smarter color selection
    
    commit 496dd7a4a5da7e210c2657b2fb54e4c00b7a4366
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Mon May 26 19:13:19 2014 -0400
    
        Fix active color
    
    commit 86be121c7be606393c2d7c0c7e981ae2a5dcaa4c
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Mon May 26 10:14:16 2014 -0400
    
        Cleanup 1
    
    commit 66f97ff7239491c92e395fbccfdbc3de509028fa
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Mon May 26 00:47:44 2014 -0400
    
        Initial import of display-settings dialog from mate-control-center. At this point, the appearance is starting to take shape, but the drag-n-drop is still not working correctly. There is still a signficant amount of cleanup work and optimization that needs to be included as well.
    
    commit f3b80bf7f311f267f31e37085b9412b0f4c1cfae
    Author: Sean Davis <smd.seandavis at gmail.com>
    Date:   Sat May 24 20:38:25 2014 -0400
    
        Replace treeview with combobox
---
 dialogs/display-settings/Makefile.am          |    9 +-
 dialogs/display-settings/display-dialog.glade |  113 +-
 dialogs/display-settings/display-name.c       |   36 +-
 dialogs/display-settings/edid-parse.c         |  444 +++---
 dialogs/display-settings/edid.h               |  206 +--
 dialogs/display-settings/foo-marshal.c        |  280 ++++
 dialogs/display-settings/foo-marshal.h        |   67 +
 dialogs/display-settings/main.c               | 1832 +++++++++++++++++++------
 dialogs/display-settings/scrollarea.c         | 1433 +++++++++++++++++++
 dialogs/display-settings/scrollarea.h         |  137 ++
 dialogs/display-settings/xfce-randr.c         |  168 +--
 dialogs/display-settings/xfce-randr.h         |   54 +-
 xfsettingsd/debug.c                           |    2 +-
 13 files changed, 3793 insertions(+), 988 deletions(-)

diff --git a/dialogs/display-settings/Makefile.am b/dialogs/display-settings/Makefile.am
index 4536033..b96d582 100644
--- a/dialogs/display-settings/Makefile.am
+++ b/dialogs/display-settings/Makefile.am
@@ -22,7 +22,11 @@ xfce4_display_settings_SOURCES = \
 	identity-popup_ui.h \
 	display-name.c \
 	edid-parse.c \
-	edid.h
+	edid.h \
+	scrollarea.c \
+	scrollarea.h \
+	foo-marshal.c \
+	foo-marshal.h
 
 xfce4_display_settings_CFLAGS = \
 	$(GTK_CFLAGS) \
@@ -43,7 +47,8 @@ xfce4_display_settings_LDADD = \
 	$(XFCONF_LIBS) \
 	$(EXO_LIBS) \
 	$(LIBX11_LIBS) -lm \
-	$(XRANDR_LIBS)
+	$(XRANDR_LIBS) \
+	$(GLIB_LIBS)
 
 if MAINTAINER_MODE
 
diff --git a/dialogs/display-settings/display-dialog.glade b/dialogs/display-settings/display-dialog.glade
index db7fcef..555e7c0 100644
--- a/dialogs/display-settings/display-dialog.glade
+++ b/dialogs/display-settings/display-dialog.glade
@@ -29,21 +29,12 @@
                 <property name="can_focus">False</property>
                 <property name="spacing">12</property>
                 <child>
-                  <object class="GtkScrolledWindow" id="scrolledwindow1">
-                    <property name="width_request">200</property>
+                  <object class="GtkAlignment" id="randr-dnd">
+                    <property name="width_request">300</property>
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">automatic</property>
-                    <property name="vscrollbar_policy">automatic</property>
-                    <property name="shadow_type">etched-in</property>
+                    <property name="can_focus">False</property>
                     <child>
-                      <object class="GtkTreeView" id="randr-outputs">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="headers_visible">False</property>
-                        <property name="enable_search">False</property>
-                        <property name="show_expanders">False</property>
-                      </object>
+                      <placeholder/>
                     </child>
                   </object>
                   <packing>
@@ -54,6 +45,7 @@
                 </child>
                 <child>
                   <object class="GtkTable" id="table1">
+                    <property name="width_request">280</property>
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="n_rows">7</property>
@@ -171,21 +163,6 @@
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkLabel" id="label-position">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">P_osition:</property>
-                        <property name="use_underline">True</property>
-                      </object>
-                      <packing>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
-                        <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                    <child>
                       <object class="GtkCheckButton" id="output-on">
                         <property name="label" translatable="yes">_Use this output</property>
                         <property name="visible">True</property>
@@ -197,6 +174,8 @@
                       </object>
                       <packing>
                         <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options">GTK_FILL</property>
                       </packing>
@@ -213,50 +192,25 @@
                       </object>
                       <packing>
                         <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
                         <property name="x_options">GTK_FILL</property>
                         <property name="y_options">GTK_FILL</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkHBox" id="hbox1">
+                      <object class="GtkComboBox" id="randr-outputs">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkComboBox" id="randr-position">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkComboBox" id="randr-active-displays">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
                       </object>
                       <packing>
-                        <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
                         <property name="y_options">GTK_FILL</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
-                    <property name="expand">True</property>
+                    <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
@@ -273,7 +227,7 @@
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkHBox" id="hbox2">
+                  <object class="GtkHBox" id="hbox1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <child>
@@ -293,17 +247,46 @@
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkToggleButton" id="identify-displays">
-                        <property name="label" translatable="yes">Identify Displays</property>
+                      <object class="GtkHButtonBox" id="hbuttonbox1">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="use_action_appearance">False</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <property name="layout_style">end</property>
+                        <child>
+                          <object class="GtkToggleButton" id="identify-displays">
+                            <property name="label" translatable="yes">Identify Displays</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_action_appearance">False</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="apply">
+                            <property name="label">gtk-apply</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
-                        <property name="expand">False</property>
+                        <property name="expand">True</property>
                         <property name="fill">True</property>
-                        <property name="pack_type">end</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
diff --git a/dialogs/display-settings/display-name.c b/dialogs/display-settings/display-name.c
index 5e8f8b1..2742566 100644
--- a/dialogs/display-settings/display-name.c
+++ b/dialogs/display-settings/display-name.c
@@ -61,7 +61,7 @@ static const struct Vendor vendors[] =
     { "SEC", "Epson" },
     { "WAC", "Wacom" },
     { "NEC", "NEC" },
-    { "CMO", "CMO" },	/* Chi Mei */
+    { "CMO", "CMO" },   /* Chi Mei */
     { "BNQ", "BenQ" },
 
     { "ABP", "Advansys" },
@@ -242,62 +242,62 @@ find_vendor (const char *code)
 
     for (i = 0; i < sizeof (vendors) / sizeof (vendors[0]); ++i)
     {
-	const Vendor *v = &(vendors[i]);
+        const Vendor *v = &(vendors[i]);
 
-	if (strcmp (v->vendor_id, code) == 0)
-	    return v->vendor_name;
+        if (strcmp (v->vendor_id, code) == 0)
+            return v->vendor_name;
     }
 
     return code;
 };
 
 char *
-make_display_name (const MonitorInfo *info)
+make_display_name (const MonitorInfo *info, guint output)
 {
     const char *vendor;
     int width_mm, height_mm, inches;
 
     if (info)
     {
-	vendor = find_vendor (info->manufacturer_code);
+        vendor = find_vendor (info->manufacturer_code);
     }
     else
     {
         /* Translators: "Unknown" here is used to identify a monitor for which
          * we don't know the vendor. When a vendor is known, the name of the
          * vendor is used. */
-	vendor = C_("Monitor vendor", "Unknown");
+        vendor = C_("Monitor vendor", "Unknown");
     }
 
     if (info && info->width_mm != -1 && info->height_mm)
     {
-	width_mm = info->width_mm;
-	height_mm = 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;
+        width_mm = info->detailed_timings[0].width_mm;
+        height_mm = info->detailed_timings[0].height_mm;
     }
     else
     {
-	width_mm = -1;
-	height_mm = -1;
+        width_mm = -1;
+        height_mm = -1;
     }
 
     if (width_mm != -1 && height_mm != -1)
     {
-	double d = sqrt (width_mm * width_mm + height_mm * height_mm);
+        double d = sqrt (width_mm * width_mm + height_mm * height_mm);
 
-	inches = (int)(d / 25.4 + 0.5);
+        inches = (int)(d / 25.4 + 0.5);
     }
     else
     {
-	inches = -1;
+        inches = -1;
     }
 
     if (inches > 0)
-	return g_strdup_printf ("%s %d\"", vendor, inches);
+        return g_strdup_printf ("%i. %s %d\"", output+1, vendor, inches);
     else
-	return g_strdup (vendor);
+        return g_strdup_printf ("%i. %s ", output+1, vendor);
 }
diff --git a/dialogs/display-settings/edid-parse.c b/dialogs/display-settings/edid-parse.c
index 3b02c37..7c35733 100644
--- a/dialogs/display-settings/edid-parse.c
+++ b/dialogs/display-settings/edid-parse.c
@@ -50,7 +50,7 @@ static int
 decode_header (const uchar *edid)
 {
     if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
-	return TRUE;
+        return TRUE;
     return FALSE;
 }
 
@@ -75,35 +75,35 @@ decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
 
     /* Serial Number */
     info->serial_number =
-	edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
+    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;
+        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];
+        info->production_year = -1;
+        info->model_year = 1990 + edid[0x11];
     }
     else
     {
-	info->production_year = 1990 + edid[0x11];
-	info->model_year = -1;
+        info->production_year = 1990 + edid[0x11];
+        info->model_year = -1;
     }
 
     return TRUE;
@@ -126,84 +126,84 @@ decode_display_parameters (const uchar *edid, MonitorInfo *info)
 
     if (info->is_digital)
     {
-	int bits;
+        int bits;
 
-	static const int bit_depth[8] =
-	{
-	    -1, 6, 8, 10, 12, 14, 16, -1
-	};
+        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
-	};
+        static const Interface interfaces[6] =
+        {
+            UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
+        };
 
-	bits = get_bits (edid[0x14], 4, 6);
-	info->connector.digital.bits_per_primary = bit_depth[bits];
+        bits = get_bits (edid[0x14], 4, 6);
+        info->connector.digital.bits_per_primary = bit_depth[bits];
 
-	bits = get_bits (edid[0x14], 0, 3);
+        bits = get_bits (edid[0x14], 0, 3);
 
-	if (bits <= 5)
-	    info->connector.digital.interface = interfaces[bits];
-	else
-	    info->connector.digital.interface = UNDEFINED;
+        if (bits <= 5)
+            info->connector.digital.interface = interfaces[bits];
+        else
+            info->connector.digital.interface = UNDEFINED;
     }
     else
     {
-	int bits = get_bits (edid[0x14], 5, 6);
+        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 },
-	};
+        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->connector.analog.video_signal_level = levels[bits][0];
-	info->connector.analog.sync_signal_level = levels[bits][1];
-	info->connector.analog.total_signal_level = levels[bits][2];
+        info->connector.analog.video_signal_level = levels[bits][0];
+        info->connector.analog.sync_signal_level = levels[bits][1];
+        info->connector.analog.total_signal_level = levels[bits][2];
 
-	info->connector.analog.blank_to_black = get_bit (edid[0x14], 4);
+        info->connector.analog.blank_to_black = get_bit (edid[0x14], 4);
 
-	info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3);
-	info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
-	info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
+        info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3);
+        info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
+        info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
 
-	info->connector.analog.serration_on_vsync = get_bit (edid[0x14], 0);
+        info->connector.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;
+        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);
+        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 */
+        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];
+        info->width_mm = 10 * edid[0x15];
+        info->height_mm = 10 * edid[0x16];
     }
 
     /* Gamma */
     if (edid[0x17] == 0xFF)
-	info->gamma = -1.0;
+        info->gamma = -1.0;
     else
-	info->gamma = (edid[0x17] + 100.0) / 100.0;
+        info->gamma = (edid[0x17] + 100.0) / 100.0;
 
     /* Features */
     info->standby = get_bit (edid[0x18], 7);
@@ -212,21 +212,21 @@ decode_display_parameters (const uchar *edid, MonitorInfo *info)
 
     if (info->is_digital)
     {
-	info->connector.digital.rgb444 = TRUE;
-	if (get_bit (edid[0x18], 3))
-	    info->connector.digital.ycrcb444 = 1;
-	if (get_bit (edid[0x18], 4))
-	    info->connector.digital.ycrcb422 = 1;
+        info->connector.digital.rgb444 = TRUE;
+        if (get_bit (edid[0x18], 3))
+            info->connector.digital.ycrcb444 = 1;
+        if (get_bit (edid[0x18], 4))
+            info->connector.digital.ycrcb422 = 1;
     }
     else
     {
-	int bits = get_bits (edid[0x18], 3, 4);
-	ColorType color_type[4] =
-	{
-	    MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
-	};
+        int bits = get_bits (edid[0x18], 3, 4);
+        ColorType color_type[4] =
+        {
+            MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
+        };
 
-	info->connector.analog.color_type = color_type[bits];
+        info->connector.analog.color_type = color_type[bits];
     }
 
     info->srgb_is_standard = get_bit (edid[0x18], 2);
@@ -248,7 +248,7 @@ decode_fraction (int high, int low)
     high = (high << 2) | low;
 
     for (i = 0; i < 10; ++i)
-	result += get_bit (high, i) * pow (2, i - 10);
+        result += get_bit (high, i) * pow (2, i - 10);
 
     return result;
 }
@@ -273,36 +273,36 @@ 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 }
-	},
+        {
+            { 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;
@@ -310,13 +310,13 @@ decode_established_timings (const uchar *edid, MonitorInfo *info)
     idx = 0;
     for (i = 0; i < 3; ++i)
     {
-	for (j = 0; j < 8; ++j)
-	{
-	    int byte = edid[0x23 + 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];
-	}
+            if (get_bit (byte, j) && established[i][j].frequency != 0)
+                info->established[idx++] = established[i][j];
+        }
     }
     return TRUE;
 }
@@ -328,26 +328,26 @@ decode_standard_timings (const uchar *edid, MonitorInfo *info)
 
     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;
-	}
+        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;
@@ -359,71 +359,71 @@ 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];
-	}
+        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)
+               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;
+        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)
+            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
+        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;
@@ -434,9 +434,9 @@ decode_detailed_timing (const uchar *timing,
     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;
+    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;
+    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];
@@ -454,26 +454,26 @@ decode_detailed_timing (const uchar *timing,
     detailed->digital_sync = get_bit (bits, 4);
     if (detailed->digital_sync)
     {
-	detailed->connector.digital.composite = !get_bit (bits, 3);
-
-	if (detailed->connector.digital.composite)
-	{
-	    detailed->connector.digital.serrations = get_bit (bits, 2);
-	    detailed->connector.digital.negative_vsync = FALSE;
-	}
-	else
-	{
-	    detailed->connector.digital.serrations = FALSE;
-	    detailed->connector.digital.negative_vsync = !get_bit (bits, 2);
-	}
-
-	detailed->connector.digital.negative_hsync = !get_bit (bits, 0);
+        detailed->connector.digital.composite = !get_bit (bits, 3);
+
+        if (detailed->connector.digital.composite)
+        {
+            detailed->connector.digital.serrations = get_bit (bits, 2);
+            detailed->connector.digital.negative_vsync = FALSE;
+        }
+        else
+        {
+            detailed->connector.digital.serrations = FALSE;
+            detailed->connector.digital.negative_vsync = !get_bit (bits, 2);
+        }
+
+        detailed->connector.digital.negative_hsync = !get_bit (bits, 0);
     }
     else
     {
-	detailed->connector.analog.bipolar = get_bit (bits, 3);
-	detailed->connector.analog.serrations = get_bit (bits, 2);
-	detailed->connector.analog.sync_on_green = !get_bit (bits, 1);
+        detailed->connector.analog.bipolar = get_bit (bits, 3);
+        detailed->connector.analog.serrations = get_bit (bits, 2);
+        detailed->connector.analog.sync_on_green = !get_bit (bits, 1);
     }
 }
 
@@ -487,17 +487,17 @@ decode_descriptors (const uchar *edid, MonitorInfo *info)
 
     for (i = 0; i < 4; ++i)
     {
-	int idx = 0x36 + i * 18;
-
-	if (edid[idx + 0] == 0x00 && edid[idx + 1] == 0x00)
-	{
-	    decode_display_descriptor (edid + idx, info);
-	}
-	else
-	{
-	    decode_detailed_timing (
-		edid + idx, &(info->detailed_timings[timing_idx++]));
-	}
+        int idx = 0x36 + i * 18;
+
+        if (edid[idx + 0] == 0x00 && edid[idx + 1] == 0x00)
+        {
+            decode_display_descriptor (edid + idx, info);
+        }
+        else
+        {
+            decode_detailed_timing (
+            edid + idx, &(info->detailed_timings[timing_idx++]));
+        }
     }
 
     info->n_detailed_timings = timing_idx;
@@ -507,13 +507,13 @@ decode_descriptors (const uchar *edid, MonitorInfo *info)
 
 static void
 decode_check_sum (const uchar *edid,
-		  MonitorInfo *info)
+          MonitorInfo *info)
 {
     int i;
     uchar check = 0;
 
     for (i = 0; i < 128; ++i)
-	check += edid[i];
+        check += edid[i];
 
     info->checksum = check;
 }
@@ -526,19 +526,19 @@ decode_edid (const uchar *edid)
     decode_check_sum (edid, info);
 
     if (decode_header (edid)
-	&& decode_vendor_and_product_identification (edid, info)
-	&& decode_edid_version (edid, info)
-	&& decode_display_parameters (edid, info)
-	&& decode_color_characteristics (edid, info)
-	&& decode_established_timings (edid, info)
-	&& decode_standard_timings (edid, info)
-	&& decode_descriptors (edid, info))
+    && decode_vendor_and_product_identification (edid, info)
+    && decode_edid_version (edid, info)
+    && decode_display_parameters (edid, info)
+    && decode_color_characteristics (edid, info)
+    && decode_established_timings (edid, info)
+    && decode_standard_timings (edid, info)
+    && decode_descriptors (edid, info))
     {
-	return info;
+        return info;
     }
     else
     {
-	g_free (info);
-	return NULL;
+        g_free (info);
+        return NULL;
     }
 }
diff --git a/dialogs/display-settings/edid.h b/dialogs/display-settings/edid.h
index 952254f..9084a7d 100644
--- a/dialogs/display-settings/edid.h
+++ b/dialogs/display-settings/edid.h
@@ -24,6 +24,8 @@
 /* Downloaded from <http://git.gnome.org/browse/gnome-desktop/tree/libgnome-desktop>
    (git commit 42452cada8cf1c4d7a81aded0a3ddbb5e97441de) */
 
+#include <glib.h>
+
 #ifndef EDID_H
 #define EDID_H
 
@@ -70,127 +72,127 @@ struct Timing
 
 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;
+    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
+        {
+            int bipolar;
+            int serrations;
+            int sync_on_green;
+        } analog;
+
+        struct
+        {
+            int composite;
+            int serrations;
+            int negative_vsync;
+            int negative_hsync;
+        } digital;
     } connector;
 };
 
 struct MonitorInfo
 {
-    int			checksum;
-    char		manufacturer_code[4];
-    int			product_code;
-    unsigned int	serial_number;
+    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         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         major_version;
+    int         minor_version;
 
-    int			is_digital;
+    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;
+        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;
     } connector;
 
-    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.
-						 */
+    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 */
+    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 MonitorInfo *info);
+char *make_display_name (const MonitorInfo *info, guint output);
 
 #endif
diff --git a/dialogs/display-settings/foo-marshal.c b/dialogs/display-settings/foo-marshal.c
new file mode 100644
index 0000000..0f4ff8f
--- /dev/null
+++ b/dialogs/display-settings/foo-marshal.c
@@ -0,0 +1,280 @@
+
+#include <glib-object.h>
+#include "foo-marshal.h"
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:OBJECT,OBJECT (marshal.list:1) */
+void
+foo_marshal_VOID__OBJECT_OBJECT (GClosure     *closure,
+                                 GValue       *return_value G_GNUC_UNUSED,
+                                 guint         n_param_values,
+                                 const GValue *param_values,
+                                 gpointer      invocation_hint G_GNUC_UNUSED,
+                                 gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__OBJECT_OBJECT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_object (param_values + 1),
+            g_marshal_value_peek_object (param_values + 2),
+            data2);
+}
+
+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */
+void
+foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure     *closure,
+                                       GValue       *return_value G_GNUC_UNUSED,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint G_GNUC_UNUSED,
+                                       gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer     data1,
+                                                          guint        arg_1,
+                                                          guint        arg_2,
+                                                          guint        arg_3,
+                                                          guint        arg_4,
+                                                          gpointer     data2);
+  register GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_uint (param_values + 1),
+            g_marshal_value_peek_uint (param_values + 2),
+            g_marshal_value_peek_uint (param_values + 3),
+            g_marshal_value_peek_uint (param_values + 4),
+            data2);
+}
+
+/* VOID:UINT,UINT (marshal.list:3) */
+void
+foo_marshal_VOID__UINT_UINT (GClosure     *closure,
+                             GValue       *return_value G_GNUC_UNUSED,
+                             guint         n_param_values,
+                             const GValue *param_values,
+                             gpointer      invocation_hint G_GNUC_UNUSED,
+                             gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer     data1,
+                                                guint        arg_1,
+                                                guint        arg_2,
+                                                gpointer     data2);
+  register GMarshalFunc_VOID__UINT_UINT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_uint (param_values + 1),
+            g_marshal_value_peek_uint (param_values + 2),
+            data2);
+}
+
+/* VOID:BOXED (marshal.list:4) */
+
+/* VOID:BOXED,BOXED (marshal.list:5) */
+void
+foo_marshal_VOID__BOXED_BOXED (GClosure     *closure,
+                               GValue       *return_value G_GNUC_UNUSED,
+                               guint         n_param_values,
+                               const GValue *param_values,
+                               gpointer      invocation_hint G_GNUC_UNUSED,
+                               gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer     data1,
+                                                  gpointer     arg_1,
+                                                  gpointer     arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_VOID__BOXED_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_boxed (param_values + 1),
+            g_marshal_value_peek_boxed (param_values + 2),
+            data2);
+}
+
+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */
+void
+foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure     *closure,
+                                         GValue       *return_value G_GNUC_UNUSED,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint G_GNUC_UNUSED,
+                                         gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_BOXED_POINTER) (gpointer     data1,
+                                                            gpointer     arg_1,
+                                                            gpointer     arg_2,
+                                                            gpointer     arg_3,
+                                                            gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_BOXED_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 4);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__POINTER_BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_boxed (param_values + 2),
+            g_marshal_value_peek_pointer (param_values + 3),
+            data2);
+}
+
+/* VOID:POINTER,POINTER (marshal.list:7) */
+void
+foo_marshal_VOID__POINTER_POINTER (GClosure     *closure,
+                                   GValue       *return_value G_GNUC_UNUSED,
+                                   guint         n_param_values,
+                                   const GValue *param_values,
+                                   gpointer      invocation_hint G_GNUC_UNUSED,
+                                   gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer     data1,
+                                                      gpointer     arg_1,
+                                                      gpointer     arg_2,
+                                                      gpointer     data2);
+  register GMarshalFunc_VOID__POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_pointer (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            data2);
+}
+
diff --git a/dialogs/display-settings/foo-marshal.h b/dialogs/display-settings/foo-marshal.h
new file mode 100644
index 0000000..cc91707
--- /dev/null
+++ b/dialogs/display-settings/foo-marshal.h
@@ -0,0 +1,67 @@
+
+#ifndef __foo_marshal_MARSHAL_H__
+#define __foo_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* VOID:OBJECT,OBJECT (marshal.list:1) */
+extern void foo_marshal_VOID__OBJECT_OBJECT (GClosure     *closure,
+                                             GValue       *return_value,
+                                             guint         n_param_values,
+                                             const GValue *param_values,
+                                             gpointer      invocation_hint,
+                                             gpointer      marshal_data);
+
+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */
+extern void foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure     *closure,
+                                                   GValue       *return_value,
+                                                   guint         n_param_values,
+                                                   const GValue *param_values,
+                                                   gpointer      invocation_hint,
+                                                   gpointer      marshal_data);
+
+/* VOID:UINT,UINT (marshal.list:3) */
+extern void foo_marshal_VOID__UINT_UINT (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
+
+/* VOID:BOXED (marshal.list:4) */
+#define foo_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED
+
+/* VOID:BOXED,BOXED (marshal.list:5) */
+extern void foo_marshal_VOID__BOXED_BOXED (GClosure     *closure,
+                                           GValue       *return_value,
+                                           guint         n_param_values,
+                                           const GValue *param_values,
+                                           gpointer      invocation_hint,
+                                           gpointer      marshal_data);
+
+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */
+extern void foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure     *closure,
+                                                     GValue       *return_value,
+                                                     guint         n_param_values,
+                                                     const GValue *param_values,
+                                                     gpointer      invocation_hint,
+                                                     gpointer      marshal_data);
+
+/* VOID:POINTER,POINTER (marshal.list:7) */
+extern void foo_marshal_VOID__POINTER_POINTER (GClosure     *closure,
+                                               GValue       *return_value,
+                                               guint         n_param_values,
+                                               const GValue *param_values,
+                                               gpointer      invocation_hint,
+                                               gpointer      marshal_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __foo_marshal_MARSHAL_H__ */
+
diff --git a/dialogs/display-settings/main.c b/dialogs/display-settings/main.c
index a6f90a2..b904213 100644
--- a/dialogs/display-settings/main.c
+++ b/dialogs/display-settings/main.c
@@ -28,6 +28,8 @@
 #include <string.h>
 #endif
 
+#include <math.h>
+
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
@@ -47,10 +49,13 @@
 #include "minimal-display-dialog_ui.h"
 #include "identity-popup_ui.h"
 
+#include "scrollarea.h"
+
+#define MARGIN  16
+
 enum
 {
     COLUMN_OUTPUT_NAME,
-    COLUMN_OUTPUT_ICON,
     COLUMN_OUTPUT_ID,
     N_OUTPUT_COLUMNS
 };
@@ -64,19 +69,10 @@ enum
 
 
 
-typedef struct _XfceRelation XfceRelation;
 typedef struct _XfceRotation XfceRotation;
 
 
 
-struct _XfceRelation
-{
-    XfceOutputRelation  relation;
-    const gchar        *name;
-};
-
-
-
 struct _XfceRotation
 {
     Rotation     rotation;
@@ -85,18 +81,6 @@ struct _XfceRotation
 
 
 
-/* Xrandr relation name conversion */
-static const XfceRelation relation_names[] =
-{
-    { XFCE_RANDR_PLACEMENT_MIRROR, N_("Same as") },
-    { XFCE_RANDR_PLACEMENT_UP,     N_("Above") },
-    { XFCE_RANDR_PLACEMENT_DOWN,   N_("Below") },
-    { XFCE_RANDR_PLACEMENT_RIGHT,  N_("Right of") },
-    { XFCE_RANDR_PLACEMENT_LEFT,   N_("Left of") }
-};
-
-
-
 /* Xrandr rotation name conversion */
 static const XfceRotation rotation_names[] =
 {
@@ -127,7 +111,6 @@ typedef struct
 } ConfirmationDialog;
 
 
-
 /* Option entries */
 static GdkNativeWindow opt_socket_id = 0;
 static gboolean opt_version = FALSE;
@@ -143,7 +126,7 @@ static GOptionEntry option_entries[] =
 /* Global xfconf channel */
 static XfconfChannel *display_channel;
 
-/* output currently selected in the treeview */
+/* output currently selected in the combobox */
 static guint active_output;
 
 /* Pointer to the used randr structure */
@@ -158,6 +141,14 @@ gboolean show_popups = FALSE;
 
 gboolean supports_alpha = FALSE;
 
+/* Graphical randr */
+GtkWidget *randr_gui_area = NULL;
+GList *current_outputs = NULL;
+
+/* Outputs Combobox TODO Use App() to store constant widgets once the cruft is cleaned */
+GtkWidget *randr_outputs_combobox = NULL;
+GtkWidget *apply_button = NULL;
+
 
 
 static void display_settings_minimal_only_display1_toggled   (GtkToggleButton *button,
@@ -172,6 +163,34 @@ static void display_settings_minimal_extend_right_toggled    (GtkToggleButton *b
 static void display_settings_minimal_only_display2_toggled   (GtkToggleButton *button,
                                                               GtkBuilder      *builder);
 
+static void
+display_settings_changed (void)
+{
+    gtk_widget_set_sensitive(GTK_WIDGET(apply_button), TRUE);
+}
+
+static XfceOutputInfo*
+get_nth_xfce_output_info(gint id)
+{
+    XfceOutputInfo *output;
+    if (current_outputs)
+        output = g_list_nth (current_outputs, id)->data;
+
+    if (output)
+        return output;
+
+    return NULL;
+}
+
+static void
+initialize_connected_outputs (void)
+{
+    if (current_outputs)
+    {
+        g_list_free(current_outputs);
+        current_outputs = NULL;
+    }
+}
 
 static guint
 display_settings_get_n_active_outputs (void)
@@ -239,7 +258,6 @@ display_settings_update_time_label (ConfirmationDialog *confirmation_dialog)
     return TRUE;
 }
 
-
 /* Returns true if the configuration is to be kept or false if it is to
  * be reverted */
 static gboolean
@@ -292,209 +310,15 @@ display_setting_timed_confirmation (GtkBuilder *main_builder)
     return ((response_id == 2) ? TRUE : FALSE);
 }
 
-
-
-static void
-display_setting_positions_changed (GtkComboBox *combobox,
-                                   GtkBuilder  *builder)
-{
-    GObject            *display_combobox;
-    XfceOutputRelation  previous_relation;
-    gint                value, selected_display, previous_related_to;
-
-    display_combobox = gtk_builder_get_object (builder, "randr-active-displays");
-
-    if (!display_setting_combo_box_get_value (combobox, &value))
-        return;
-
-    if (!display_setting_combo_box_get_value (GTK_COMBO_BOX (display_combobox),
-                                              &selected_display))
-        return;
-
-    /* Skip if the display combobox hasn't made a selection yet */
-    if (selected_display == -1) return;
-
-    /* back up */
-    previous_relation = xfce_randr->relation[active_output];
-    previous_related_to = xfce_randr->related_to[active_output];
-
-    /* Save changes to currently active display */
-    xfce_randr->relation[active_output] = value;
-    xfce_randr->related_to[active_output] = selected_display;
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, TRUE);
-
-    /* Save changes to selected display */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            selected_display, FALSE);
-
-    /* Apply all changes */
-    xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-    /* Ask user confirmation */
-    if (!display_setting_timed_confirmation (builder))
-    {
-        /* Restore the currently active display */
-        xfce_randr->relation[active_output] = previous_relation;
-        xfce_randr->related_to[active_output] = previous_related_to;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, TRUE);
-        /* Restore the selected display */
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                selected_display, FALSE);
-
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-    }
-}
-
-
-
-static void
-display_setting_positions_populate (GtkBuilder *builder)
-{
-    GtkTreeModel *model;
-    GObject      *combobox, *label, *mirror_displays;
-    GtkTreeIter   iter;
-    guint         n;
-
-    /* Get the positions combo box store and clear it */
-    combobox = gtk_builder_get_object (builder, "randr-position");
-    model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
-    gtk_list_store_clear (GTK_LIST_STORE (model));
-
-    label = gtk_builder_get_object (builder, "label-position");
-    if (xfce_randr->noutput > 1)
-    {
-        gtk_widget_show (GTK_WIDGET (label));
-        gtk_widget_show (GTK_WIDGET (combobox));
-    }
-    else
-    {
-        gtk_widget_hide (GTK_WIDGET (label));
-        gtk_widget_hide (GTK_WIDGET (combobox));
-        return;
-    }
-
-    /* Only make the combobox interactive if there is more than one output,
-       and if they are not in mirror mode */
-    mirror_displays = gtk_builder_get_object (builder, "mirror-displays");
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (mirror_displays)) ||
-        display_settings_get_n_active_outputs () <= 1)
-    {
-        gtk_widget_set_sensitive (GTK_WIDGET (combobox), FALSE);
-        return;
-    }
-    gtk_widget_set_sensitive (GTK_WIDGET (combobox), TRUE);
-
-    /* Block the "changed" signal to avoid triggering the confirmation dialog */
-    g_signal_handlers_block_by_func (combobox, display_setting_positions_changed,
-                                     builder);
-
-    /* Try to insert the relations */
-    for (n = 0; n < G_N_ELEMENTS (relation_names); n++)
-    {
-        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-        gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-                            COLUMN_COMBO_NAME, _(relation_names[n].name),
-                            COLUMN_COMBO_VALUE, relation_names[n].relation, -1);
-
-        /* Select the active relation */
-        if (relation_names[n].relation == xfce_randr->relation[active_output])
-            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &iter);
-    }
-
-    /* Unblock the signal */
-    g_signal_handlers_unblock_by_func (combobox, display_setting_positions_changed,
-                                       builder);
-}
-
-static void
-display_setting_active_displays_changed (GtkComboBox *combobox,
-                                         GtkBuilder  *builder)
-{
-    GObject *position_combobox;
-    gint     value;
-
-    if (!display_setting_combo_box_get_value (combobox, &value))
-        return;
-
-    position_combobox = gtk_builder_get_object (builder, "randr-position");
-
-    display_setting_positions_changed (GTK_COMBO_BOX (position_combobox), builder);
-}
-
-static void
-display_setting_active_displays_populate (GtkBuilder *builder)
-{
-    GtkTreeModel *model;
-    GObject      *combobox, *mirror_displays;
-    guint         n;
-    GtkTreeIter   iter;
-
-    /* Get the active-displays combo box store and clear it */
-    combobox = gtk_builder_get_object (builder, "randr-active-displays");
-    model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
-    gtk_list_store_clear (GTK_LIST_STORE (model));
-
-    if (xfce_randr->noutput > 1)
-        gtk_widget_show (GTK_WIDGET (combobox));
-    else
-    {
-        gtk_widget_hide (GTK_WIDGET (combobox));
-        return;
-    }
-
-    /* Only make the combobox interactive if there is more than one output,
-       and if they are not in mirror mode */
-    mirror_displays = gtk_builder_get_object (builder, "mirror-displays");
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (mirror_displays)) ||
-        display_settings_get_n_active_outputs () <= 1)
-    {
-        gtk_widget_set_sensitive (GTK_WIDGET (combobox), FALSE);
-        return;
-    }
-    gtk_widget_set_sensitive (GTK_WIDGET (combobox), TRUE);
-
-    /* Block the "changed" signal to avoid triggering the confirmation dialog */
-    g_signal_handlers_block_by_func (combobox, display_setting_active_displays_changed,
-                                     builder);
-
-    /* Insert all active displays */
-    for (n = 0; n < xfce_randr->noutput; ++n)
-    {
-        if (xfce_randr->mode[n] == None || n == active_output)
-            continue;
-
-        /* Insert display name */
-        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-        gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-                            COLUMN_COMBO_NAME, xfce_randr->friendly_name[n],
-                            COLUMN_COMBO_VALUE, n, -1);
-
-        /* Select the active related output */
-        if (n == xfce_randr->related_to[active_output])
-            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &iter);
-    }
-
-    /* Unblock the signal */
-    g_signal_handlers_unblock_by_func (combobox, display_setting_active_displays_changed,
-                                       builder);
-}
-
-
-
 static void
 display_setting_reflections_changed (GtkComboBox *combobox,
                                      GtkBuilder  *builder)
 {
     gint value;
-    Rotation old_rotation;
 
     if (!display_setting_combo_box_get_value (combobox, &value))
         return;
 
-    old_rotation = xfce_randr->rotation[active_output];
-
     /* Remove existing reflection */
     xfce_randr->rotation[active_output] &= ~XFCE_RANDR_REFLECTIONS_MASK;
 
@@ -502,21 +326,10 @@ display_setting_reflections_changed (GtkComboBox *combobox,
     xfce_randr->rotation[active_output] |= value;
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, FALSE);
-    xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-    /* Ask user confirmation */
-    if (!display_setting_timed_confirmation (builder))
-    {
-        xfce_randr->rotation[active_output] = old_rotation;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, FALSE);
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-    }
+    display_settings_changed ();
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
 }
 
-
 static void
 display_setting_reflections_populate (GtkBuilder *builder)
 {
@@ -576,40 +389,27 @@ display_setting_reflections_populate (GtkBuilder *builder)
                                        builder);
 }
 
-
-
 static void
 display_setting_rotations_changed (GtkComboBox *combobox,
                                    GtkBuilder  *builder)
 {
-    Rotation old_rotation;
+    XfceOutputInfo *output;
     gint value;
 
     if (!display_setting_combo_box_get_value (combobox, &value))
         return;
 
     /* Set new rotation */
-    old_rotation = xfce_randr->rotation[active_output];
     xfce_randr->rotation[active_output] &= ~XFCE_RANDR_ROTATIONS_MASK;
     xfce_randr->rotation[active_output] |= value;
+    output = get_nth_xfce_output_info(active_output);
+    output->rotation = xfce_randr->rotation[active_output];
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, TRUE);
-    xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-    /* Ask user confirmation */
-    if (!display_setting_timed_confirmation (builder))
-    {
-        xfce_randr->rotation[active_output] = old_rotation;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, TRUE);
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-    }
+    display_settings_changed ();
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
 }
 
-
-
 static void
 display_setting_rotations_populate (GtkBuilder *builder)
 {
@@ -666,39 +466,22 @@ display_setting_rotations_populate (GtkBuilder *builder)
                                        builder);
 }
 
-
-
 static void
 display_setting_refresh_rates_changed (GtkComboBox *combobox,
                                        GtkBuilder  *builder)
 {
-    RRMode old_mode;
     gint value;
 
     if (!display_setting_combo_box_get_value (combobox, &value))
         return;
 
     /* Set new mode */
-    old_mode = xfce_randr->mode[active_output];
     xfce_randr->mode[active_output] = value;
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, FALSE);
-    xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-    /* Ask user confirmation */
-    if (!display_setting_timed_confirmation (builder))
-    {
-        xfce_randr->mode[active_output] = old_mode;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, FALSE);
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-    }
+    display_settings_changed ();
 }
 
-
-
 static void
 display_setting_refresh_rates_populate (GtkBuilder *builder)
 {
@@ -771,36 +554,30 @@ static void
 display_setting_resolutions_changed (GtkComboBox *combobox,
                                      GtkBuilder  *builder)
 {
-    RRMode old_mode;
+    XfceOutputInfo *output;
+    const XfceRRMode *mode;
     gint value;
 
     if (!display_setting_combo_box_get_value (combobox, &value))
         return;
 
     /* Set new resolution */
-    old_mode = xfce_randr->mode[active_output];
     xfce_randr->mode[active_output] = value;
 
+    /* Apply resolution to gui */
+    output = get_nth_xfce_output_info (active_output);
+    mode = xfce_randr_find_mode_by_id (xfce_randr, active_output, value);
+    output->width = mode->width;
+    output->height = mode->height;
+
     /* Update refresh rates */
     display_setting_refresh_rates_populate (builder);
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, TRUE);
-    xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-    /* Ask user confirmation */
-    if (!display_setting_timed_confirmation (builder))
-    {
-        xfce_randr->mode[active_output] = old_mode;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, TRUE);
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-    }
+    display_settings_changed ();
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
 }
 
-
-
 static void
 display_setting_resolutions_populate (GtkBuilder *builder)
 {
@@ -991,7 +768,6 @@ display_setting_identity_popup_expose (GtkWidget      *popup,
     return FALSE;
 }
 
-
 static GtkWidget *
 display_setting_identity_display (gint display_id)
 {
@@ -1090,23 +866,13 @@ static void
 display_setting_mirror_displays_toggled (GtkToggleButton *togglebutton,
                                          GtkBuilder      *builder)
 {
-    GObject *positions, *active_displays;
-    guint    n;
+    XfceOutputInfo *output;
+    guint    n, pos = 0;
     RRMode   mode;
 
     if (!xfce_randr)
         return;
 
-    positions = gtk_builder_get_object (builder, "randr-position");
-    active_displays = gtk_builder_get_object (builder, "randr-active-displays");
-
-    if (display_settings_get_n_active_outputs () <= 1)
-    {
-        gtk_widget_set_sensitive (GTK_WIDGET (positions), FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET (active_displays), FALSE);
-        return;
-    }
-
     if (gtk_toggle_button_get_active (togglebutton))
     {
         /* Activate mirror-mode with a single mode for all of them */
@@ -1119,18 +885,11 @@ display_setting_mirror_displays_toggled (GtkToggleButton *togglebutton,
 
             if (mode != None)
                 xfce_randr->mode[n] = mode;
-            xfce_randr->relation[n] = XFCE_RANDR_PLACEMENT_MIRROR;
-            xfce_randr->related_to[n] = active_output;
             xfce_randr->rotation[n] = RR_Rotate_0;
-            xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                    n, TRUE);
+            xfce_randr->mirrored[n] = TRUE;
+            xfce_randr->position[n].x = 0;
+            xfce_randr->position[n].y = 0;
         }
-
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
-
-        /* Disable the position comboboxes */
-        gtk_widget_set_sensitive (GTK_WIDGET (positions), FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET (active_displays), FALSE);
     }
     else
     {
@@ -1138,18 +897,30 @@ display_setting_mirror_displays_toggled (GtkToggleButton *togglebutton,
         for (n = 0; n < xfce_randr->noutput; n++)
         {
             xfce_randr->mode[n] = xfce_randr_preferred_mode (xfce_randr, n);
-            xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                    n, TRUE);
-        }
+            xfce_randr->mirrored[n] = FALSE;
+            xfce_randr->position[n].x = pos;
+            xfce_randr->position[n].y = 0;
 
-        xfce_randr_apply (xfce_randr, "Default", display_channel);
+            pos = xfce_randr_mode_width (xfce_randr_find_mode_by_id (xfce_randr, n, xfce_randr->mode[n]), 0);
+        }
+    }
 
-        /* Re-enable the position comboboxes */
-        gtk_widget_set_sensitive (GTK_WIDGET (positions), TRUE);
-        gtk_widget_set_sensitive (GTK_WIDGET (active_displays), TRUE);
+    /* Apply resolution to gui */
+    for (n = 0; n < xfce_randr->noutput; n++)
+    {
+        output = get_nth_xfce_output_info (n);
+        output->rotation = xfce_randr->rotation[n];
+        output->x = xfce_randr->position[n].x;
+        output->y = xfce_randr->position[n].y;
+        output->mirrored = xfce_randr->mirrored[n];
+        output->width = xfce_randr_mode_width (xfce_randr_find_mode_by_id (xfce_randr, n, xfce_randr->mode[n]), 0);
+        output->height = xfce_randr_mode_height (xfce_randr_find_mode_by_id (xfce_randr, n, xfce_randr->mode[n]), 0);
     }
-}
 
+    /* Apply the changes */
+    display_settings_changed ();
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
+}
 
 static void
 display_setting_mirror_displays_populate (GtkBuilder *builder)
@@ -1194,7 +965,7 @@ display_setting_mirror_displays_populate (GtkBuilder *builder)
             continue;
 
         cloned &= (xfce_randr->mode[n] == mode &&
-                   xfce_randr->relation[n] == XFCE_RANDR_PLACEMENT_MIRROR);
+                   xfce_randr->mirrored[n]);
 
         if (!cloned)
             break;
@@ -1207,8 +978,6 @@ display_setting_mirror_displays_populate (GtkBuilder *builder)
                                        builder);
 }
 
-
-
 static void
 display_setting_output_toggled (GtkToggleButton *togglebutton,
                                 GtkBuilder      *builder)
@@ -1247,21 +1016,22 @@ display_setting_output_toggled (GtkToggleButton *togglebutton,
     }
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                            active_output, FALSE);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, active_output);
     xfce_randr_apply (xfce_randr, "Default", display_channel);
 
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
+
     /* Ask user confirmation */
     if (!display_setting_timed_confirmation (builder))
     {
         xfce_randr->mode[active_output] = old_mode;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                active_output, FALSE);
+        xfce_randr_save_output (xfce_randr, "Default", display_channel, active_output);
         xfce_randr_apply (xfce_randr, "Default", display_channel);
+
+        foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
     }
 }
 
-
 static void
 display_setting_output_status_populate (GtkBuilder *builder)
 {
@@ -1290,23 +1060,19 @@ display_setting_output_status_populate (GtkBuilder *builder)
                                        builder);
 }
 
-
-
 static void
-display_settings_treeview_selection_changed (GtkTreeSelection *selection,
-                                             GtkBuilder       *builder)
+display_settings_combobox_selection_changed (GtkComboBox *combobox,
+                                             GtkBuilder  *builder)
 {
     GtkTreeModel *model;
     GtkTreeIter   iter;
     GtkWidget    *popup;
-    gboolean      has_selection;
     gint          active_id, previous_id;
 
-    /* Get the selection */
-    has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
-    if (G_LIKELY (has_selection))
+    if (gtk_combo_box_get_active_iter (combobox, &iter))
     {
         /* Get the output info */
+        model = gtk_combo_box_get_model (combobox);
         gtk_tree_model_get (model, &iter, COLUMN_OUTPUT_ID, &active_id, -1);
 
         /* Get the new active screen or output */
@@ -1314,8 +1080,6 @@ display_settings_treeview_selection_changed (GtkTreeSelection *selection,
         active_output = active_id;
 
         /* Update the combo boxes */
-        display_setting_positions_populate (builder);
-        display_setting_active_displays_populate (builder);
         display_setting_output_status_populate (builder);
         display_setting_mirror_displays_populate (builder);
         display_setting_resolutions_populate (builder);
@@ -1330,66 +1094,48 @@ display_settings_treeview_selection_changed (GtkTreeSelection *selection,
         popup = g_hash_table_lookup (display_popups, GINT_TO_POINTER (active_id));
         if (popup)
             gtk_widget_queue_draw (popup);
+
+        if (randr_gui_area)
+            foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
     }
 }
 
-
-
 static void
-display_settings_treeview_populate (GtkBuilder *builder)
+display_settings_combobox_populate (GtkBuilder *builder)
 {
     guint             m;
     GtkListStore     *store;
-    GObject          *treeview;
+    GObject          *combobox;
     GtkTreeIter       iter;
-    GdkPixbuf        *display_icon, *lucent_display_icon;
-    GtkTreeSelection *selection;
     gboolean          selected = FALSE;
 
     /* Create a new list store */
     store = gtk_list_store_new (N_OUTPUT_COLUMNS,
                                 G_TYPE_STRING, /* COLUMN_OUTPUT_NAME */
-                                GDK_TYPE_PIXBUF, /* COLUMN_OUTPUT_ICON */
                                 G_TYPE_INT);   /* COLUMN_OUTPUT_ID */
 
-    /* Set the treeview model */
-    treeview = gtk_builder_get_object (builder, "randr-outputs");
-    gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
-
-    /* Get the display icon */
-    display_icon =
-        gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), "display-icon",
-                                  32,
-                                  GTK_ICON_LOOKUP_GENERIC_FALLBACK,
-                                  NULL);
-
-    lucent_display_icon = NULL;
+    /* Set up the new combobox which will replace the above combobox */
+    combobox = gtk_builder_get_object (builder, "randr-outputs");
+    gtk_combo_box_set_model (GTK_COMBO_BOX (combobox), GTK_TREE_MODEL (store));
 
     /* Walk all the connected outputs */
     for (m = 0; m < xfce_randr->noutput; ++m)
     {
-        if (xfce_randr->mode[m] == None && lucent_display_icon == NULL)
-            lucent_display_icon =
-                exo_gdk_pixbuf_lucent (display_icon, 50);
-
         /* Insert the output in the store */
         gtk_list_store_append (store, &iter);
         if (xfce_randr->mode[m] == None)
             gtk_list_store_set (store, &iter,
                                 COLUMN_OUTPUT_NAME, xfce_randr->friendly_name[m],
-                                COLUMN_OUTPUT_ICON, lucent_display_icon,
                                 COLUMN_OUTPUT_ID, m, -1);
         else
             gtk_list_store_set (store, &iter,
                                 COLUMN_OUTPUT_NAME, xfce_randr->friendly_name[m],
-                                COLUMN_OUTPUT_ICON, display_icon,
                                 COLUMN_OUTPUT_ID, m, -1);
 
         /* Select active output */
         if (m == active_output)
         {
-            gtk_tree_selection_select_iter (selection, &iter);
+            gtk_combo_box_set_active (GTK_COMBO_BOX(combobox), m);
             selected = TRUE;
         }
     }
@@ -1397,19 +1143,12 @@ display_settings_treeview_populate (GtkBuilder *builder)
     /* If nothing was selected the active output is no longer valid,
      * select the last display in the list. */
     if (!selected)
-        gtk_tree_selection_select_iter (selection, &iter);
+        gtk_combo_box_set_active (GTK_COMBO_BOX(combobox), m);
 
     /* Release the store */
     g_object_unref (G_OBJECT (store));
-
-    /* Release the icons */
-    g_object_unref (display_icon);
-    if (lucent_display_icon != NULL)
-        g_object_unref (lucent_display_icon);
 }
 
-
-
 static void
 display_settings_combo_box_create (GtkComboBox *combobox)
 {
@@ -1428,8 +1167,6 @@ display_settings_combo_box_create (GtkComboBox *combobox)
     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox), renderer, "text", COLUMN_COMBO_NAME);
 }
 
-
-
 static void
 display_settings_dialog_response (GtkDialog  *dialog,
                                   gint        response_id,
@@ -1477,29 +1214,33 @@ on_identify_displays_toggled (GtkWidget *widget, GtkBuilder *builder)
     set_display_popups_visible (show_popups);
 }
 
+static void
+display_setting_apply (GtkWidget *widget, GtkBuilder *builder)
+{
+    guint i = 0;
+
+    for (i=0; i < xfce_randr->noutput; i++)
+        xfce_randr_save_output (xfce_randr, "Default", display_channel, i);
+    xfce_randr_apply (xfce_randr, "Default", display_channel);
 
+    /* TODO: Restore Confirmation Dialog */
+    gtk_widget_set_sensitive(widget, FALSE);
+}
 
 static GtkWidget *
 display_settings_dialog_new (GtkBuilder *builder)
 {
-    GObject          *treeview;
-    GtkCellRenderer  *renderer;
-    GtkTreeSelection *selection;
     GObject          *combobox;
+    GtkCellRenderer  *renderer;
     GObject          *label, *check, *mirror, *identify;
 
-    /* Get the treeview */
-    treeview = gtk_builder_get_object (builder, "randr-outputs");
-    gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (treeview), COLUMN_OUTPUT_NAME);
-
-    /* Icon renderer */
-    renderer = gtk_cell_renderer_pixbuf_new ();
-    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), 0, "", renderer, "pixbuf", COLUMN_OUTPUT_ICON, NULL);
-    g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DND, NULL);
+    /* Get the combobox */
+    combobox = gtk_builder_get_object (builder, "randr-outputs");
 
     /* Text renderer */
     renderer = gtk_cell_renderer_text_new ();
-    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), 1, "", renderer, "text", COLUMN_OUTPUT_NAME, NULL);
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE);
+    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), renderer, "text", COLUMN_OUTPUT_NAME, NULL);
     g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
     /* Identification popups */
@@ -1508,10 +1249,8 @@ display_settings_dialog_new (GtkBuilder *builder)
     g_signal_connect (G_OBJECT (identify), "toggled", G_CALLBACK (on_identify_displays_toggled), builder);
     set_display_popups_visible (show_popups);
 
-    /* Treeview selection */
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
-    gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
-    g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (display_settings_treeview_selection_changed), builder);
+    /* Display selection combobox */
+    g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (display_settings_combobox_selection_changed), builder);
 
     /* Setup the combo boxes */
     check = gtk_builder_get_object (builder, "output-on");
@@ -1537,14 +1276,6 @@ display_settings_dialog_new (GtkBuilder *builder)
     gtk_widget_show (GTK_WIDGET (combobox));
     g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (display_setting_reflections_changed), builder);
 
-    combobox = gtk_builder_get_object (builder, "randr-position");
-    display_settings_combo_box_create (GTK_COMBO_BOX (combobox));
-    g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (display_setting_positions_changed), builder);
-
-    combobox = gtk_builder_get_object (builder, "randr-active-displays");
-    display_settings_combo_box_create (GTK_COMBO_BOX (combobox));
-    g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (display_setting_active_displays_changed), builder);
-
     combobox = gtk_builder_get_object (builder, "randr-resolution");
     display_settings_combo_box_create (GTK_COMBO_BOX (combobox));
     g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (display_setting_resolutions_changed), builder);
@@ -1561,8 +1292,12 @@ display_settings_dialog_new (GtkBuilder *builder)
     xfconf_g_property_bind (display_channel, "/Notify", G_TYPE_BOOLEAN, check,
                             "active");
 
-    /* Populate the treeview */
-    display_settings_treeview_populate (builder);
+    apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "apply"));
+    g_signal_connect (G_OBJECT (apply_button), "clicked", G_CALLBACK (display_setting_apply), builder);
+    gtk_widget_set_sensitive(apply_button, FALSE);
+
+    /* Populate the combobox */
+    display_settings_combobox_populate (builder);
 
     return GTK_WIDGET (gtk_builder_get_object (builder, "display-dialog"));
 }
@@ -1590,8 +1325,8 @@ display_settings_minimal_only_display1_toggled (GtkToggleButton *button,
     xfce_randr->mode[1] = None;
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0, FALSE);
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1, FALSE);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1);
     xfce_randr_apply (xfce_randr, "Default", display_channel);
 
     gtk_widget_set_sensitive (GTK_WIDGET(buttons), TRUE);
@@ -1620,8 +1355,8 @@ display_settings_minimal_only_display2_toggled (GtkToggleButton *button,
     xfce_randr->mode[0] = None;
 
     /* Apply the changes */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0, FALSE);
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1, FALSE);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1);
     xfce_randr_apply (xfce_randr, "Default", display_channel);
 
     gtk_widget_set_sensitive (GTK_WIDGET(buttons), TRUE);
@@ -1646,7 +1381,7 @@ display_settings_minimal_mirror_displays_toggled (GtkToggleButton *button,
 
     buttons = gtk_builder_get_object (builder, "buttons");
     gtk_widget_set_sensitive (GTK_WIDGET(buttons), FALSE);
-    
+
     /* Activate mirror-mode with a single mode for all of them */
     mode = xfce_randr_clonable_mode (xfce_randr);
     /* Configure each available display for mirroring */
@@ -1657,11 +1392,11 @@ display_settings_minimal_mirror_displays_toggled (GtkToggleButton *button,
 
         if (mode != None)
             xfce_randr->mode[n] = mode;
-        xfce_randr->relation[n] = XFCE_RANDR_PLACEMENT_MIRROR;
-        xfce_randr->related_to[n] = 0;
+        xfce_randr->mirrored[n] = TRUE;
         xfce_randr->rotation[n] = RR_Rotate_0;
-        xfce_randr_save_output (xfce_randr, "Default", display_channel,
-                                n, TRUE);
+        xfce_randr->position[n].x = 0;
+        xfce_randr->position[n].y = 0;
+        xfce_randr_save_output (xfce_randr, "Default", display_channel, n);
     }
 
     /* Apply all changes */
@@ -1674,6 +1409,7 @@ static void
 display_settings_minimal_extend_right_toggled (GtkToggleButton *button,
                                                GtkBuilder      *builder)
 {
+    const XfceRRMode *mode;
     GObject *buttons;
     guint    n;
 
@@ -1699,16 +1435,17 @@ display_settings_minimal_extend_right_toggled (GtkToggleButton *button,
     }
 
     /* (Re)set Display1 to 0x0 */
-    xfce_randr->relation[0] = 0;
-    xfce_randr->related_to[0] = 0;
+    xfce_randr->position[0].x = 0;
+    xfce_randr->position[0].y = 0;
 
     /* Move Display2 right of Display1 */
-    xfce_randr->relation[1] = XFCE_RANDR_PLACEMENT_RIGHT;
-    xfce_randr->related_to[1] = 0;
+    mode = xfce_randr_find_mode_by_id (xfce_randr, 0, xfce_randr->mode[0]);
+    xfce_randr->position[1].x = mode->width;
+    xfce_randr->position[1].y = 0;
 
     /* Save changes to both displays */
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0, FALSE);
-    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1, TRUE);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 0);
+    xfce_randr_save_output (xfce_randr, "Default", display_channel, 1);
 
     /* Apply all changes */
     xfce_randr_apply (xfce_randr, "Default", display_channel);
@@ -1716,8 +1453,6 @@ display_settings_minimal_extend_right_toggled (GtkToggleButton *button,
     gtk_widget_set_sensitive (GTK_WIDGET (buttons), TRUE);
 }
 
-
-
 static GdkFilterReturn
 screen_on_event (GdkXEvent *xevent,
                  GdkEvent  *event,
@@ -1735,24 +1470,1207 @@ screen_on_event (GdkXEvent *xevent,
     if (event_num == RRScreenChangeNotify)
     {
         xfce_randr_reload (xfce_randr);
-        display_settings_treeview_populate (builder);
+        display_settings_combobox_populate (builder);
 
         /* recreate the identify display popups */
         g_hash_table_destroy (display_popups);
         display_setting_identity_popups_populate ();
+        set_display_popups_visible(show_popups);
     }
 
+    initialize_connected_outputs();
+    foo_scroll_area_invalidate (FOO_SCROLL_AREA (randr_gui_area));
+
     /* Pass the event on to GTK+ */
     return GDK_FILTER_CONTINUE;
 }
 
-static void
-display_settings_show_main_dialog (GdkDisplay *display)
+/* Xfce RANDR GUI **TODO** Place these functions in a sensible location */
+static gboolean
+get_mirrored_configuration (void)
 {
-    GtkBuilder  *builder;
-    GtkWidget   *dialog, *plug;
-    GObject     *plug_child;
+    gboolean cloned = FALSE;
+    RRMode   mode = None;
+    guint    n;
+
+    if (!xfce_randr)
+        return FALSE;
+
+    if (!xfce_randr->noutput > 1)
+        return FALSE;
+
+    /* Can outputs be cloned? */
+    if (display_settings_get_n_active_outputs () > 1)
+        mode = xfce_randr_clonable_mode (xfce_randr);
+
+    if (mode == None)
+        return FALSE;
+
+    /* Check if mirror settings are on */
+    for (n = 0; n < xfce_randr->noutput; n++)
+    {
+        if (xfce_randr->mode[n] == None)
+            continue;
+
+        cloned = xfce_randr->mirrored[n];
+
+        if (!cloned)
+            break;
+    }
+
+    return cloned;
+}
+
+static XfceOutputInfo *convert_xfce_output_info (gint output_id)
+{
+    XfceOutputInfo *output;
+    const XfceRRMode *mode, *preferred;
+    RRMode preferred_mode;
+    gint x, y;
+
+    xfce_randr_get_positions(xfce_randr, output_id, &x, &y);
+    mode = xfce_randr_find_mode_by_id (xfce_randr, output_id, xfce_randr->mode[output_id]);
+    preferred_mode = xfce_randr_preferred_mode(xfce_randr, output_id);
+    preferred = xfce_randr_find_mode_by_id (xfce_randr, output_id, preferred_mode);
+    output = g_new0 (XfceOutputInfo, 1);
+    output->id = output_id;
+    output->x = x;
+    output->y = y;
+    output->user_data = NULL;
+    output->display_name = xfce_randr->friendly_name[output_id];
+    output->connected = TRUE;
+    output->on = xfce_randr->mode[output_id] != None;
+    output->pref_width = preferred->width;
+    output->pref_height = preferred->height;
+    if (output->on)
+    {
+        output->rotation = xfce_randr->rotation[output_id];
+        output->width = mode->width;
+        output->height = mode->height;
+        output->rate = mode->rate;
+    }
+    else
+    {
+        output->rotation = 0;
+        output->width = output->pref_width;
+        output->height = output->pref_height;
+        output->rate = 0.0;
+    }
+
+    return output;
+}
+
+typedef struct App App;
+typedef struct GrabInfo GrabInfo;
+
+struct App
+{
+    XfceOutputInfo     *current_output;
+
+    GtkWidget          *dialog;
+};
+
+static gboolean output_overlaps (XfceOutputInfo *output);
+static void get_geometry (XfceOutputInfo *output, int *w, int *h);
+
+static void
+lay_out_outputs_horizontally (void)
+{
+    gint x, y;
+    GList *list;
+
+    /* Lay out all the monitors horizontally when "mirror screens" is turned
+     * off, to avoid having all of them overlapped initially.  We put the
+     * outputs turned off on the right-hand side.
+     */
+
+    x = 0;
+    y = 0;
+
+    /* First pass, all "on" outputs */
+    for (list = current_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output;
+
+        output = list->data;
+        if (output->connected && output->on)
+        {
+            if ((gint)output->x + (gint)output->width > x || output->y > y)
+            {
+                y = output->y;
+                x = output->x + output->width;
+            }
+        }
+    }
+
+    /* Second pass, all the black screens */
+    for (list = current_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output;
+
+        output = list->data;
+        if (!(output->connected && output->on))
+        {
+            output->x = x;
+            output->y = y;
+            x += output->width;
+        }
+    }
+}
+
+static void
+on_viewport_changed (FooScrollArea *scroll_area,
+                     GdkRectangle  *old_viewport,
+                     GdkRectangle  *new_viewport)
+{
+    foo_scroll_area_set_size (scroll_area,
+                              new_viewport->width,
+                              new_viewport->height);
+
+    foo_scroll_area_invalidate (scroll_area);
+}
+
+static void
+layout_set_font (PangoLayout *layout, const char *font)
+{
+    PangoFontDescription *desc =
+        pango_font_description_from_string (font);
+
+    if (desc)
+    {
+        pango_layout_set_font_description (layout, desc);
+
+        pango_font_description_free (desc);
+    }
+}
+
+static void
+get_geometry (XfceOutputInfo *output, int *w, int *h)
+{
+    if (output->on)
+    {
+        *h = output->height;
+        *w = output->width;
+    }
+    else
+    {
+        *h = output->pref_height;
+        *w = output->pref_width;
+    }
+    if ((output->rotation == RR_Rotate_90) || (output->rotation == RR_Rotate_270))
+    {
+        int tmp;
+        tmp = *h;
+        *h = *w;
+        *w = tmp;
+    }
+}
+
+static void
+initialize_connected_outputs_at_zero(void)
+{
+    GList *list = NULL;
+    gint start_x, start_y;
+
+    start_x = G_MAXINT;
+    start_y = G_MAXINT;
+
+    /* Get the left-most and top-most coordinates */
+    for (list = current_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output = list->data;
+
+        start_x = MIN(start_x, output->x);
+        start_y = MIN(start_y, output->y);
+    }
+
+    /* Realign at zero */
+    for (list = current_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output = list->data;
+
+        output->y = output->y - start_y;
+        output->x = output->x - start_x;
+
+        /* Update the Xfce Randr */
+        xfce_randr->position[output->id].x = output->x;
+        xfce_randr->position[output->id].y = output->y;
+    }
+}
+
+static GList *
+list_connected_outputs (gint *total_w, gint *total_h)
+{
+    gint dummy;
+    guint m;
+    GList *list = NULL;
+
+    if (!total_w)
+        total_w = &dummy;
+    if (!total_h)
+        total_h = &dummy;
+
+    /* Do we need to initialize the current outputs? */
+    if (!current_outputs)
+    {
+        for (m = 0; m < xfce_randr->noutput; ++m)
+        {
+            XfceOutputInfo *output = convert_xfce_output_info(m);
+
+            current_outputs = g_list_prepend (current_outputs, output);
+        }
+        current_outputs = g_list_reverse (current_outputs);
+
+        lay_out_outputs_horizontally();
+    }
+
+    *total_w = 0;
+    *total_h = 0;
+
+    for (list = current_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output = list->data;
+
+        int w, h;
+
+        get_geometry (output, &w, &h);
+
+        *total_w = MAX(*total_w, w + output->x);
+        *total_h = MAX(*total_h, h + output->y);
+    }
+
+    return current_outputs;
+}
+
+static int
+get_n_connected (void)
+{
+    return xfce_randr->noutput;
+}
+
+static double
+compute_scale (void)
+{
+    int available_w, available_h;
+    gint total_w, total_h;
+    GdkRectangle viewport;
+
+    foo_scroll_area_get_viewport (FOO_SCROLL_AREA (randr_gui_area), &viewport);
+
+    list_connected_outputs (&total_w, &total_h);
+
+    available_w = viewport.width - 2 * MARGIN;
+    available_h = viewport.height - 2 * MARGIN;
+
+    return MIN ((double)available_w / (double)total_w, (double)available_h / (double)total_h);
+}
+
+typedef struct Edge
+{
+    XfceOutputInfo *output;
+    int x1, y1;
+    int x2, y2;
+} Edge;
+
+typedef struct Snap
+{
+    Edge *snapper;      /* Edge that should be snapped */
+    Edge *snappee;
+    int dy, dx;
+} Snap;
+
+static void
+add_edge (XfceOutputInfo *output, int x1, int y1, int x2, int y2, GArray *edges)
+{
+    Edge e;
+
+    e.x1 = x1;
+    e.x2 = x2;
+    e.y1 = y1;
+    e.y2 = y2;
+    e.output = output;
+
+    g_array_append_val (edges, e);
+}
+
+static void
+list_edges_for_output (XfceOutputInfo *output, GArray *edges)
+{
+    int x, y, w, h;
+
+    x = output->x;
+    y = output->y;
+    get_geometry (output, &w, &h);
+
+    /* Top, Bottom, Left, Right */
+    add_edge (output, x, y, x + w, y, edges);
+    add_edge (output, x, y + h, x + w, y + h, edges);
+    add_edge (output, x, y, x, y + h, edges);
+    add_edge (output, x + w, y, x + w, y + h, edges);
+}
+
+static void
+list_edges (GArray *edges)
+{
+    GList *connected_outputs = NULL;
+    GList *list;
+
+    connected_outputs = list_connected_outputs (NULL, NULL);
+
+    for (list = connected_outputs; list != NULL; list = list->next)
+    {
+        list_edges_for_output (list->data, edges);
+    }
+}
+
+static gboolean
+overlap (int s1, int e1, int s2, int e2)
+{
+    return (!(e1 < s2 || s1 >= e2));
+}
+
+static gboolean
+horizontal_overlap (Edge *snapper, Edge *snappee)
+{
+    if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2)
+        return FALSE;
+
+    return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2);
+}
+
+static gboolean
+vertical_overlap (Edge *snapper, Edge *snappee)
+{
+    if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2)
+        return FALSE;
+
+    return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2);
+}
+
+static void
+add_snap (GArray *snaps, Snap snap)
+{
+    if (ABS (snap.dx) <= 200 || ABS (snap.dy) <= 200)
+        g_array_append_val (snaps, snap);
+}
+
+static void
+add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps)
+{
+    Snap snap;
+
+    snap.snapper = snapper;
+    snap.snappee = snappee;
+
+    if (horizontal_overlap (snapper, snappee))
+    {
+        snap.dx = 0;
+        snap.dy = snappee->y1 - snapper->y1;
+
+        add_snap (snaps, snap);
+    }
+    else if (vertical_overlap (snapper, snappee))
+    {
+        snap.dy = 0;
+        snap.dx = snappee->x1 - snapper->x1;
+
+        add_snap (snaps, snap);
+    }
+
+    /* Corner snaps */
+    /* 1->1 */
+    snap.dx = snappee->x1 - snapper->x1;
+    snap.dy = snappee->y1 - snapper->y1;
+
+    add_snap (snaps, snap);
+
+    /* 1->2 */
+    snap.dx = snappee->x2 - snapper->x1;
+    snap.dy = snappee->y2 - snapper->y1;
+
+    add_snap (snaps, snap);
+
+    /* 2->2 */
+    snap.dx = snappee->x2 - snapper->x2;
+    snap.dy = snappee->y2 - snapper->y2;
+
+    add_snap (snaps, snap);
+
+    /* 2->1 */
+    snap.dx = snappee->x1 - snapper->x2;
+    snap.dy = snappee->y1 - snapper->y2;
+
+    add_snap (snaps, snap);
+}
+
+static void
+list_snaps (XfceOutputInfo *output, GArray *edges, GArray *snaps)
+{
+    guint i;
+
+    for (i = 0; i < edges->len; ++i)
+    {
+        Edge *output_edge = &(g_array_index (edges, Edge, i));
+
+        if (output_edge->output == output)
+        {
+            guint j;
+
+            for (j = 0; j < edges->len; ++j)
+            {
+                Edge *edge = &(g_array_index (edges, Edge, j));
+
+                if (edge->output != output)
+                    add_edge_snaps (output_edge, edge, snaps);
+            }
+        }
+    }
+}
+
+static gboolean
+corner_on_edge (int x, int y, Edge *e)
+{
+    if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2)
+        return TRUE;
+
+    if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2)
+        return TRUE;
+
+    return FALSE;
+}
+
+static gboolean
+edges_align (Edge *e1, Edge *e2)
+{
+    if (corner_on_edge (e1->x1, e1->y1, e2))
+        return TRUE;
+
+    if (corner_on_edge (e2->x1, e2->y1, e1))
+        return TRUE;
+
+    return FALSE;
+}
+
+static gboolean
+output_is_aligned (XfceOutputInfo *output, GArray *edges)
+{
+    gboolean result = FALSE;
+    guint i;
+
+    for (i = 0; i < edges->len; ++i)
+    {
+        Edge *output_edge = &(g_array_index (edges, Edge, i));
+
+        if (output_edge->output == output)
+        {
+            guint j;
+
+            for (j = 0; j < edges->len; ++j)
+            {
+                Edge *edge = &(g_array_index (edges, Edge, j));
+
+                /* We are aligned if an output edge matches
+                 * an edge of another output
+                 */
+                if (edge->output != output_edge->output)
+                {
+                    if (edges_align (output_edge, edge))
+                    {
+                        result = TRUE;
+                        goto done;
+                    }
+                }
+            }
+        }
+    }
+done:
+    return result;
+}
+
+static void
+get_output_rect (XfceOutputInfo *output, GdkRectangle *rect)
+{
+    int w, h;
+
+    get_geometry (output, &w, &h);
+
+    rect->width = w;
+    rect->height = h;
+    rect->x = output->x;
+    rect->y = output->y;
+}
+
+static gboolean
+output_overlaps (XfceOutputInfo *output)
+{
+    GList         *connected_outputs = NULL;
+    GList         *list;
+    gboolean       overlap = FALSE;
+    GdkRectangle   output_rect;
+
+    get_output_rect (output, &output_rect);
+
+    connected_outputs = list_connected_outputs (NULL, NULL);
+
+    for (list = connected_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *other = list->data;
+        if (other != output)
+        {
+            GdkRectangle other_rect;
+
+            get_output_rect (other, &other_rect);
+            if (gdk_rectangle_intersect (&output_rect, &other_rect, NULL))
+            {
+                overlap = TRUE;
+                break;
+            }
+        }
+    }
+
+    return overlap;
+}
+
+static gboolean
+xfce_rr_config_is_aligned (GArray *edges)
+{
+    GList     *connected_outputs = NULL;
+    GList     *list;
+    gboolean   aligned = TRUE;
+
+    connected_outputs = list_connected_outputs (NULL, NULL);
+
+    for (list = connected_outputs; list != NULL; list = list->next)
+    {
+        XfceOutputInfo *output = list->data;
+        if (!output_is_aligned (output, edges) || output_overlaps (output))
+        {
+            aligned = FALSE;
+            break;
+        }
+    }
+
+    return aligned;
+}
+
+struct GrabInfo
+{
+    int grab_x;
+    int grab_y;
+    int output_x;
+    int output_y;
+};
+
+static gboolean
+is_corner_snap (const Snap *s)
+{
+    return s->dx != 0 && s->dy != 0;
+}
+
+static int
+compare_snaps (gconstpointer v1, gconstpointer v2)
+{
+    const Snap *s1 = v1;
+    const Snap *s2 = v2;
+    int sv1 = MAX (ABS (s1->dx), ABS (s1->dy));
+    int sv2 = MAX (ABS (s2->dx), ABS (s2->dy));
+    int d;
+
+    d = sv1 - sv2;
+
+    /* This snapping algorithm is good enough for rock'n'roll, but
+     * this is probably a better:
+     *
+     *    First do a horizontal/vertical snap, then
+     *    with the new coordinates from that snap,
+     *    do a corner snap.
+     *
+     * Right now, it's confusing that corner snapping
+     * depends on the distance in an axis that you can't actually see.
+     *
+     */
+    if (d == 0)
+    {
+        if (is_corner_snap (s1) && !is_corner_snap (s2))
+            return -1;
+        else if (is_corner_snap (s2) && !is_corner_snap (s1))
+            return 1;
+        else
+            return 0;
+    }
+    else
+    {
+        return d;
+    }
+}
+
+/* Sets a mouse cursor for a widget's window.  As a hack, you can pass
+ * GDK_BLANK_CURSOR to mean "set the cursor to NULL" (i.e. reset the widget's
+ * window's cursor to its default).
+ */
+static void
+set_cursor (GtkWidget *widget, GdkCursorType type)
+{
+    GdkCursor *cursor;
+    GdkWindow *window;
+
+    if (type == GDK_BLANK_CURSOR)
+        cursor = NULL;
+    else
+        cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), type);
+
+    window = gtk_widget_get_window (widget);
+
+    if (window)
+        gdk_window_set_cursor (window, cursor);
+
+    if (cursor)
+        gdk_cursor_unref (cursor);
+}
+
+static void
+set_monitors_tooltip (gchar *tooltip_text)
+{
+    const char *text;
+
+    if (tooltip_text)
+        text = g_strdup(tooltip_text);
+
+    else
+        text = _("Select a monitor to change its properties; drag it to rearrange its placement.");
+
+    gtk_widget_set_tooltip_text (randr_gui_area, text);
+}
+
+static void
+on_output_event (FooScrollArea      *area,
+                 FooScrollAreaEvent *event,
+                 gpointer            data)
+{
+    XfceOutputInfo *output = data;
+
+    //App *app = g_object_get_data (G_OBJECT (area), "app");
+
+    /* If the mouse is inside the outputs, set the cursor to "you can move me".  See
+     * on_canvas_event() for where we reset the cursor to the default if it
+     * exits the outputs' area.
+     */
+    if (!get_mirrored_configuration() && get_n_connected() > 1)
+        set_cursor (GTK_WIDGET (area), GDK_FLEUR);
+
+    if (event->type == FOO_BUTTON_PRESS)
+    {
+        GrabInfo *info;
+
+        gtk_combo_box_set_active (GTK_COMBO_BOX(randr_outputs_combobox), output->id);
+
+        if (!get_mirrored_configuration() && get_n_connected() > 1)
+        {
+            foo_scroll_area_begin_grab (area, on_output_event, data);
+
+            info = g_new0 (GrabInfo, 1);
+            info->grab_x = event->x;
+            info->grab_y = event->y;
+            info->output_x = output->x;
+            info->output_y = output->y;
+
+            set_monitors_tooltip (g_strdup_printf(_("(%i, %i)"), output->x, output->y) );
+
+            output->user_data = info;
+        }
+
+        foo_scroll_area_invalidate (area);
+    }
+    else
+    {
+        if (foo_scroll_area_is_grabbed (area))
+        {
+            GrabInfo *info = output->user_data;
+            double scale = compute_scale();
+            int new_x, new_y;
+            guint i;
+            GArray *edges, *snaps;
+
+            new_x = info->output_x + (event->x - info->grab_x) / scale;
+            new_y = info->output_y + (event->y - info->grab_y) / scale;
+
+            output->x = new_x;
+            output->y = new_y;
+
+            edges = g_array_new (TRUE, TRUE, sizeof (Edge));
+            snaps = g_array_new (TRUE, TRUE, sizeof (Snap));
+
+            list_edges (edges);
+            list_snaps (output, edges, snaps);
+
+            g_array_sort (snaps, compare_snaps);
+
+            output->x = info->output_x;
+            output->y = info->output_y;
+
+            for (i = 0; i < snaps->len; ++i)
+            {
+                Snap *snap = &(g_array_index (snaps, Snap, i));
+                GArray *new_edges = g_array_new (TRUE, TRUE, sizeof (Edge));
+
+                output->x = new_x + snap->dx;
+                output->y = new_y + snap->dy;
+
+                g_array_set_size (new_edges, 0);
+                list_edges (new_edges);
+
+                if (xfce_rr_config_is_aligned (new_edges))
+                {
+                    g_array_free (new_edges, TRUE);
+                    break;
+                }
+                else
+                {
+                    output->x = info->output_x;
+                    output->y = info->output_y;
+                    g_array_free (new_edges, TRUE);
+                }
+            }
+
+            g_array_free (snaps, TRUE);
+            g_array_free (edges, TRUE);
+
+            if (event->type == FOO_BUTTON_RELEASE)
+            {
+                foo_scroll_area_end_grab (area);
+                set_monitors_tooltip (NULL);
+
+                g_free (output->user_data);
+                output->user_data = NULL;
+
+                initialize_connected_outputs_at_zero();
+                display_settings_changed ();
+            }
+            else
+            {
+                set_monitors_tooltip (g_strdup_printf(_("(%i, %i)"), output->x, output->y) );
+            }
+
+            foo_scroll_area_invalidate (area);
+        }
+    }
+}
+
+static void
+on_canvas_event (FooScrollArea      *area,
+                 FooScrollAreaEvent *event,
+                 gpointer            data)
+{
+    /* If the mouse exits the outputs, reset the cursor to the default.  See
+     * on_output_event() for where we set the cursor to the movement cursor if
+     * it is over one of the outputs.
+     */
+    set_cursor (GTK_WIDGET (area), GDK_BLANK_CURSOR);
+}
+
+static PangoLayout *
+get_display_name (XfceOutputInfo *output)
+{
+    const char *text;
+
+    if (get_mirrored_configuration())
+    {
+    /* Translators:  this is the feature where what you see on your laptop's
+     * screen is the same as your external monitor.  Here, "Mirror" is being
+     * used as an adjective, not as a verb.  For example, the Spanish
+     * translation could be "Pantallas en Espejo", *not* "Espejar Pantallas".
+     */
+        text = _("Mirror Screens");
+    }
+    else
+    {
+        text = output->display_name;
+    }
+
+    return gtk_widget_create_pango_layout (
+    GTK_WIDGET (randr_gui_area), text);
+}
+
+static void
+paint_background (FooScrollArea *area,
+                  cairo_t       *cr)
+{
+    GdkRectangle viewport;
+    GtkWidget *widget;
+    GtkStyle *widget_style;
+
+    widget = GTK_WIDGET (area);
+
+    foo_scroll_area_get_viewport (area, &viewport);
+    widget_style = gtk_widget_get_style (widget);
+
+    cairo_set_source_rgb (cr,
+                          widget_style->base[GTK_STATE_NORMAL].red / 65535.0,
+                          widget_style->base[GTK_STATE_NORMAL].green / 65535.0,
+                          widget_style->base[GTK_STATE_NORMAL].blue / 65535.0);
+
+    cairo_rectangle (cr,
+                     viewport.x, viewport.y,
+                     viewport.width, viewport.height);
+
+    cairo_fill_preserve (cr);
+
+    foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL);
+
+    cairo_set_source_rgb (cr,
+                          widget_style->dark[GTK_STATE_NORMAL].red / 65535.0,
+                          widget_style->dark[GTK_STATE_NORMAL].green / 65535.0,
+                          widget_style->dark[GTK_STATE_NORMAL].blue / 65535.0);
+
+    cairo_stroke (cr);
+}
+
+static void
+paint_output (cairo_t *cr, int i, double *snap_x, double *snap_y)
+{
+    int w, h;
+    double scale = compute_scale();
+    double x, y, end_x, end_y;
+    gint total_w, total_h;
+    GList *connected_outputs = list_connected_outputs (&total_w, &total_h);
+    XfceOutputInfo *output = g_list_nth (connected_outputs, i)->data;
+    PangoLayout *layout = get_display_name (output);
+    PangoRectangle ink_extent, log_extent;
+    GdkRectangle viewport;
+    cairo_pattern_t *pat_lin = NULL, *pat_radial = NULL;
+    double alpha = 1.0;
+    double available_w;
+    double factor = 1.0;
+
+    cairo_save (cr);
+
+    foo_scroll_area_get_viewport (FOO_SCROLL_AREA (randr_gui_area), &viewport);
+
+    get_geometry (output, &w, &h);
+
+    viewport.height -= 2 * MARGIN;
+    viewport.width -= 2 * MARGIN;
+
+    /* Center the displayed outputs in the viewport */
+    x = ceil (output->x * scale + MARGIN + (viewport.width - total_w * scale) / 2.0);
+    y = ceil (output->y * scale + MARGIN + (viewport.height - total_h * scale) / 2.0);
+
+    /* Align endpoints */
+    end_x = x + ceil (w * scale);
+    end_y = y + ceil (h * scale);
+    if ( abs((int)end_x-(int)*snap_x) <= 1 )
+    {
+        end_x = *snap_x;
+    }
+    if ( abs((int)end_y-(int)*snap_y) <= 1 )
+    {
+        end_y = *snap_y;
+    }
+    *snap_x = end_x;
+    *snap_y = end_y;
+
+    cairo_translate (cr,
+                     x + (w * scale) / 2,
+                     y + (h * scale) / 2);
+
+    /* rotation is already applied in get_geometry */
+
+    if (output->rotation == RR_Reflect_X)
+        cairo_scale (cr, -1, 1);
+
+    if (output->rotation == RR_Reflect_Y)
+        cairo_scale (cr, 1, -1);
+
+    cairo_translate (cr,
+                     - x - (w * scale) / 2,
+                     - y - (h * scale) / 2);
+
+    cairo_rectangle (cr, x, y, end_x - x, end_y - y);
+    cairo_clip_preserve (cr);
+
+    foo_scroll_area_add_input_from_fill (FOO_SCROLL_AREA (randr_gui_area),
+                                         cr, on_output_event, output);
+
+    cairo_set_line_width (cr, 1.0);
+
+    if (output->id != active_output)
+        alpha = 0.8;
+
+    if (output->on)
+    {
+        /* Background gradient for active display */
+        pat_lin = cairo_pattern_create_linear(x, y, x, y + (h * scale));
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.0, 0.56, 0.85, 0.92, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.2, 0.33, 0.75, 0.92, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.7, 0.25, 0.57, 0.77, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 1.0, 0.17, 0.39, 0.63, alpha);
+        cairo_set_source (cr, pat_lin);
+        cairo_fill_preserve (cr);
+
+        cairo_set_source_rgba (cr, 0.11, 0.26, 0.52, alpha);
+        cairo_stroke (cr);
+    }
+    else
+    {
+        /* Background gradient for disabled display */
+        pat_lin = cairo_pattern_create_linear(x, y, x, y + (h * scale));
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.0, 0.24, 0.3, 0.31, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.2, 0.17, 0.20, 0.22, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 0.7, 0.14, 0.16, 0.18, alpha);
+        cairo_pattern_add_color_stop_rgba(pat_lin, 1.0, 0.07, 0.07, 0.07, alpha);
+        cairo_set_source (cr, pat_lin);
+        cairo_fill_preserve (cr);
+
+        cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, alpha);
+        cairo_stroke (cr);
+    }
+
+    /* Draw inner stroke */
+    cairo_rectangle (cr, x + 1.5, y + 1.5, end_x - x - 3, end_y - y - 3);
+    cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, alpha - 0.75);
+    cairo_stroke (cr);
+
+    /* Draw reflection as radial gradient on a polygon */
+    pat_radial = cairo_pattern_create_radial ((end_x -x) /2 + x, y, 1, (end_x -x) /2 + x, y, h * scale);
+    cairo_pattern_add_color_stop_rgba(pat_radial, 0.0, 1.0, 1.0, 1.0, 0.4);
+    cairo_pattern_add_color_stop_rgba(pat_radial, 0.5, 1.0, 1.0, 1.0, 0.15);
+    cairo_pattern_add_color_stop_rgba(pat_radial, 0.8, 1.0, 1.0, 1.0, 0.0);
+
+    cairo_move_to (cr, x + 1.5, y + 1.5);
+    cairo_line_to (cr, end_x - 1.5, y + 1.5);
+    cairo_line_to (cr, end_x - 1.5, y + (end_y - y) / 3);
+    cairo_line_to (cr, x + 1.5, y + (end_y - y) / 1.5);
+    cairo_close_path (cr);
+    cairo_set_source (cr, pat_radial);
+    cairo_fill (cr);
+
+    /* Display name label*/
+    layout_set_font (layout, "Sans Bold 12");
+    pango_layout_get_pixel_extents (layout, &ink_extent, &log_extent);
+
+    available_w = w * scale + 0.5 - 6; /* Same as the inner rectangle's width, minus 1 pixel of padding on each side */
+
+    cairo_scale (cr, factor, factor);
+
+    if (available_w < ink_extent.width)
+    {
+        pango_layout_set_width (layout, available_w * PANGO_SCALE);
+        pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
+        log_extent.width = available_w;
+    }
+
+    cairo_move_to (cr,
+                   x + ((w * scale + 0.5) - factor * log_extent.width) / 2,
+                   y + ((h * scale + 0.5) - factor * log_extent.height) / 2 - 1);
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, alpha - 0.6);
+
+    pango_cairo_show_layout (cr, layout);
+
+    cairo_move_to (cr,
+                   x + ((w * scale + 0.5) - factor * log_extent.width) / 2,
+                   y + ((h * scale + 0.5) - factor * log_extent.height) / 2);
+
+    cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, alpha);
+
+    pango_cairo_show_layout (cr, layout);
+
+    /* Display state label */
+    if (!output->on)
+    {
+        PangoLayout *display_state;
+        display_state = gtk_widget_create_pango_layout ( GTK_WIDGET (randr_gui_area), _("(Disabled)"));
+        layout_set_font (display_state, "Sans 9");
+        pango_layout_get_pixel_extents (display_state, &ink_extent, &log_extent);
+
+        available_w = w * scale + 0.5 - 6;
+        if (available_w < ink_extent.width)
+            factor = available_w / ink_extent.width;
+        else
+            factor = 1.0;
+        cairo_move_to (cr,
+                       x + ((w * scale + 0.5) - factor * log_extent.width) / 2,
+                       y + ((h * scale + 0.5) - factor * log_extent.height) / 2 + 18);
+        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.75);
+        pango_cairo_show_layout (cr, display_state);
+        g_object_unref (display_state);
+    }
+
+    cairo_restore (cr);
+
+    if (pat_lin)
+        cairo_pattern_destroy(pat_lin);
+    if (pat_radial)
+        cairo_pattern_destroy(pat_radial);
+
+    g_object_unref (layout);
+}
+
+static void
+on_area_paint (FooScrollArea *area,
+               cairo_t       *cr,
+               GdkRectangle  *extent,
+               GdkRegion     *region,
+               gpointer       data)
+{
+    GList *connected_outputs = NULL;
+    GList *list;
+    double x, y;
+
+    paint_background (area, cr);
+
+    connected_outputs = list_connected_outputs (NULL, NULL);
+
+    for (list = connected_outputs; list != NULL; list = list->next)
+    {
+        paint_output (cr, g_list_position (connected_outputs, list), &x, &y);
+
+        if (get_mirrored_configuration())
+            break;
+    }
+}
+
+static XfceOutputInfo *
+get_nearest_output (gint x, gint y)
+{
+    int nearest_index;
+    guint nearest_dist;
+    guint m;
+
+    nearest_index = -1;
+    nearest_dist = G_MAXINT;
+
+    /* Walk all the connected outputs */
+    for (m = 0; m < xfce_randr->noutput; ++m)
+    {
+        XfceOutputInfo *output;
+        guint dist_x, dist_y;
+
+        output = convert_xfce_output_info(m);
+
+        if (!(output->connected && output->on))
+            continue;
+
+        if (x < output->x)
+            dist_x = output->x - x;
+        else if (x >= output->x + (gint)output->width)
+            dist_x = x - (output->x + output->width) + 1;
+        else
+            dist_x = 0;
+
+        if (y < output->y)
+            dist_y = output->y - y;
+        else if (y >= output->y + (gint)output->height)
+            dist_y = y - (output->y + output->height) + 1;
+        else
+            dist_y = 0;
+
+        if (MIN (dist_x, dist_y) < nearest_dist)
+        {
+            nearest_dist = MIN (dist_x, dist_y);
+            nearest_index = m;
+        }
+    }
+
+    if (nearest_index != -1)
+        return convert_xfce_output_info(nearest_index);
+    else
+        return NULL;
+}
+
+/* Gets the output that contains the largest intersection with the window.
+ * Logic stolen from gdk_screen_get_monitor_at_window().
+ */
+static XfceOutputInfo *
+get_output_for_window (GdkWindow *window)
+{
+    GdkRectangle win_rect;
+    int largest_area;
+    int largest_index;
+    guint m;
+
+    gdk_window_get_geometry (window, &win_rect.x, &win_rect.y, &win_rect.width, &win_rect.height, NULL);
+    gdk_window_get_origin (window, &win_rect.x, &win_rect.y);
+
+    largest_area = 0;
+    largest_index = -1;
+
+    /* Walk all the connected outputs */
+    for (m = 0; m < xfce_randr->noutput; ++m)
+    {
+        XfceOutputInfo *output;
+        GdkRectangle output_rect, intersection;
+
+        output = convert_xfce_output_info(m);
+
+        output_rect.x      = output->x;
+        output_rect.y      = output->y;
+        output_rect.width  = output->width;
+        output_rect.height = output->height;
+
+        if (xfce_randr->mode[m] != None)
+        {
+            if (gdk_rectangle_intersect (&win_rect, &output_rect, &intersection))
+            {
+                int area;
+
+                area = intersection.width * intersection.height;
+                if (area > largest_area)
+                {
+                    largest_area = area;
+                    largest_index = m;
+                }
+            }
+        }
+    }
+
+    if (largest_index != -1)
+        return convert_xfce_output_info(largest_index);
+    else
+        return get_nearest_output ( win_rect.x + win_rect.width / 2,
+                                    win_rect.y + win_rect.height / 2);
+}
+
+/* We select the current output, i.e. select the one being edited, based on
+ * which output is showing the configuration dialog.
+ */
+static void
+select_current_output_from_dialog_position (App *app)
+{
+    if (gtk_widget_get_realized (app->dialog))
+        app->current_output = get_output_for_window (gtk_widget_get_window (app->dialog));
+    else
+        app->current_output = NULL;
+}
+
+/* This is a GtkWidget::map-event handler.  We wait for the display-properties
+ * dialog to be mapped, and then we select the output which corresponds to the
+ * monitor on which the dialog is being shown.
+ */
+static gboolean
+dialog_map_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
+{
+    App *app = data;
+
+    select_current_output_from_dialog_position (app);
+    return FALSE;
+}
+
+static GtkWidget*
+_gtk_builder_get_widget (GtkBuilder *builder, const gchar *name)
+{
+    return GTK_WIDGET (gtk_builder_get_object (builder, name));
+}
+/* Xfce RANDR GUI */
+
+static void
+display_settings_show_main_dialog (GdkDisplay *display)
+{
+    GtkBuilder  *builder;
+    GtkWidget   *dialog, *plug;
+    GObject     *plug_child;
     GError      *error = NULL;
+    GtkWidget   *gui_container;
+    App *app;
 
     /* Load the Gtk user-interface file */
     builder = gtk_builder_new ();
@@ -1769,11 +2687,39 @@ display_settings_show_main_dialog (GdkDisplay *display)
                                               randr_event_base,
                                               RRNotify + 1);
         gdk_window_add_filter (gdk_get_default_root_window (), screen_on_event, builder);
-        
+
         /* Show/Hide the helper popups when the dialog is shown/hidden */
         g_signal_connect(G_OBJECT(dialog), "focus-out-event", G_CALLBACK (focus_out_event), builder);
         g_signal_connect(G_OBJECT(dialog), "focus-in-event", G_CALLBACK (focus_in_event), builder);
 
+        app = g_new0 (App, 1);
+
+        initialize_connected_outputs();
+
+        app->dialog = _gtk_builder_get_widget (builder, "display-dialog");
+        g_signal_connect_after (app->dialog, "map-event",
+                    G_CALLBACK (dialog_map_event_cb), app);
+
+        /* Scroll Area */
+        randr_gui_area = (GtkWidget *)foo_scroll_area_new ();
+        randr_outputs_combobox = _gtk_builder_get_widget (builder, "randr-outputs");
+
+        g_object_set_data (G_OBJECT (randr_gui_area), "app", app);
+
+        set_monitors_tooltip (NULL);
+
+        /* FIXME: this should be computed dynamically */
+        foo_scroll_area_set_min_size (FOO_SCROLL_AREA (randr_gui_area), -1, 200);
+        gtk_widget_show (randr_gui_area);
+        g_signal_connect (randr_gui_area, "paint",
+                  G_CALLBACK (on_area_paint), app);
+        g_signal_connect (randr_gui_area, "viewport_changed",
+                  G_CALLBACK (on_viewport_changed), app);
+
+        gui_container = GTK_WIDGET(gtk_builder_get_object(builder, "randr-dnd"));
+        gtk_container_add(GTK_CONTAINER(gui_container), GTK_WIDGET(randr_gui_area));
+        gtk_widget_show_all(gui_container);
+
         if (G_UNLIKELY (opt_socket_id == 0))
         {
             g_signal_connect (G_OBJECT (dialog), "response",
@@ -1869,6 +2815,7 @@ display_settings_show_minimal_dialog (GdkDisplay *display)
     GObject    *only_display1, *only_display2, *mirror_displays;
     GObject    *extend_right, *advanced, *fake_button, *label;
     GError     *error = NULL;
+    gboolean    found = FALSE;
     RRMode      mode;
 
     builder = gtk_builder_new ();
@@ -1910,30 +2857,39 @@ display_settings_show_minimal_dialog (GdkDisplay *display)
             label = gtk_builder_get_object (builder, "label4");
             gtk_label_set_text (GTK_LABEL (label), xfce_randr->friendly_name[1]);
             gtk_widget_set_tooltip_text(GTK_WIDGET(label), xfce_randr->friendly_name[1]);
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (only_display2),
-                                          xfce_randr->mode[1] != None);
-                                          
+
             /* Can outputs be cloned? */
             if (display_settings_get_n_active_outputs () > 1)
                 mode = xfce_randr_clonable_mode (xfce_randr);
-
             gtk_widget_set_sensitive (GTK_WIDGET (mirror_displays), mode != None);
 
-            if (xfce_randr->mode[0] != None && xfce_randr->mode[1] != None)
+            if (xfce_randr->mode[0] != None)
+            {
+                if (xfce_randr->mode[1] != None)
+                {
+                    /* Check for mirror */
+                    if (xfce_randr->mirrored[0] && xfce_randr->mirrored[1])
+                    {
+                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mirror_displays), TRUE);
+                        found = TRUE;
+                    }
+                    /* Check for Extend Right */
+                    if (!found && (gint)xfce_randr->position[1].x == (gint)xfce_randr->position[0].x + (gint)xfce_randr_find_mode_by_id (xfce_randr, 0, xfce_randr->mode[0])->width)
+                    {
+                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extend_right), TRUE);
+                        found = TRUE;
+                    }
+                }
+                /* Toggle Primary */
+                if (!found)
+                {
+                    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (only_display1), TRUE);
+                }
+            }
+            /* Toggle Secondary */
+            else
             {
-                /* Check for mirror */
-                if ((xfce_randr->relation[1] == XFCE_RANDR_PLACEMENT_MIRROR &&
-                    xfce_randr->related_to[1] == 0) || (xfce_randr->related_to[0] == 1 &&
-                    xfce_randr->relation[0] == XFCE_RANDR_PLACEMENT_MIRROR))
-                    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mirror_displays),
-                                                  TRUE);
-
-                /* Check for Extend Right */
-                if ((xfce_randr->relation[1] == XFCE_RANDR_PLACEMENT_RIGHT &&
-                    xfce_randr->related_to[1] == 0) || (xfce_randr->related_to[0] == 1 &&
-                    xfce_randr->relation[1] == XFCE_RANDR_PLACEMENT_LEFT))
-                    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (extend_right),
-                                                  TRUE);
+                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (only_display2), TRUE);
             }
         }
         else
diff --git a/dialogs/display-settings/scrollarea.c b/dialogs/display-settings/scrollarea.c
new file mode 100644
index 0000000..4e61aa5
--- /dev/null
+++ b/dialogs/display-settings/scrollarea.c
@@ -0,0 +1,1433 @@
+/* Copyright 2006, 2007, 2008, Soren Sandmann <sandmann at daimi.au.dk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gdk/gdkprivate.h> /* For GDK_PARENT_RELATIVE_BG */
+#include "scrollarea.h"
+#include "foo-marshal.h"
+
+G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER);
+
+static GtkWidgetClass *parent_class;
+
+typedef struct BackingStore BackingStore;
+
+typedef void (* ExposeFunc) (cairo_t *cr, GdkRegion *region, gpointer data);
+
+typedef struct InputPath InputPath;
+typedef struct InputRegion InputRegion;
+typedef struct AutoScrollInfo AutoScrollInfo;
+
+struct InputPath
+{
+    gboolean                is_stroke;
+    cairo_fill_rule_t       fill_rule;
+    double                  line_width;
+    cairo_path_t           *path;       /* In canvas coordinates */
+
+    FooScrollAreaEventFunc  func;
+    gpointer                data;
+
+    InputPath              *next;
+};
+
+/* InputRegions are mutually disjoint */
+struct InputRegion
+{
+    GdkRegion *region;      /* the boundary of this area in canvas coordinates */
+    InputPath *paths;
+};
+
+struct AutoScrollInfo
+{
+    int             dx;
+    int             dy;
+    int             timeout_id;
+    int             begin_x;
+    int             begin_y;
+    double          res_x;
+    double          res_y;
+    GTimer         *timer;
+};
+
+struct FooScrollAreaPrivate
+{
+    GdkWindow              *input_window;
+
+    int                     width;
+    int                     height;
+
+    GtkAdjustment          *hadj;
+    GtkAdjustment          *vadj;
+    int                     x_offset;
+    int                     y_offset;
+
+    int                     min_width;
+    int                     min_height;
+
+    GPtrArray              *input_regions;
+
+    AutoScrollInfo         *auto_scroll_info;
+
+    /* During expose, this region is set to the region
+     * being exposed. At other times, it is NULL
+     *
+     * It is used for clipping of input areas
+     */
+    GdkRegion              *expose_region;
+    InputRegion            *current_input;
+
+    gboolean                grabbed;
+    FooScrollAreaEventFunc  grab_func;
+    gpointer                grab_data;
+
+    GdkPixmap              *pixmap;
+    GdkRegion              *update_region;      /* In canvas coordinates */
+};
+
+enum
+{
+    VIEWPORT_CHANGED,
+    PAINT,
+    INPUT,
+    LAST_SIGNAL,
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+static void foo_scroll_area_size_request (GtkWidget      *widget,
+                                          GtkRequisition *requisition);
+
+static gboolean foo_scroll_area_expose (GtkWidget      *widget,
+                                        GdkEventExpose *expose);
+
+static void foo_scroll_area_size_allocate (GtkWidget     *widget,
+                                           GtkAllocation *allocation);
+
+static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+                                                    GtkAdjustment *hadjustment,
+                                                    GtkAdjustment *vadjustment);
+
+static void foo_scroll_area_realize (GtkWidget *widget);
+static void foo_scroll_area_unrealize (GtkWidget *widget);
+static void foo_scroll_area_map (GtkWidget *widget);
+static void foo_scroll_area_unmap (GtkWidget *widget);
+static gboolean foo_scroll_area_button_press (GtkWidget      *widget,
+                                              GdkEventButton *event);
+static gboolean foo_scroll_area_button_release (GtkWidget      *widget,
+                                                GdkEventButton *event);
+static gboolean foo_scroll_area_motion (GtkWidget      *widget,
+                                        GdkEventMotion *event);
+
+static void
+foo_scroll_area_map (GtkWidget *widget)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+    if (area->priv->input_window)
+        gdk_window_show (area->priv->input_window);
+}
+
+static void
+foo_scroll_area_unmap (GtkWidget *widget)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    if (area->priv->input_window)
+        gdk_window_hide (area->priv->input_window);
+
+    GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+foo_scroll_area_finalize (GObject *object)
+{
+    FooScrollArea *scroll_area = FOO_SCROLL_AREA (object);
+
+    g_object_unref (scroll_area->priv->hadj);
+    g_object_unref (scroll_area->priv->vadj);
+
+    g_ptr_array_free (scroll_area->priv->input_regions, TRUE);
+
+    g_free (scroll_area->priv);
+
+    G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object);
+}
+
+static void
+foo_scroll_area_class_init (FooScrollAreaClass *class)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (class);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+    object_class->finalize = foo_scroll_area_finalize;
+
+    widget_class->size_request = foo_scroll_area_size_request;
+    widget_class->expose_event = foo_scroll_area_expose;
+
+    widget_class->size_allocate = foo_scroll_area_size_allocate;
+    widget_class->realize = foo_scroll_area_realize;
+    widget_class->unrealize = foo_scroll_area_unrealize;
+    widget_class->button_press_event = foo_scroll_area_button_press;
+    widget_class->button_release_event = foo_scroll_area_button_release;
+    widget_class->motion_notify_event = foo_scroll_area_motion;
+    widget_class->map = foo_scroll_area_map;
+    widget_class->unmap = foo_scroll_area_unmap;
+
+    class->set_scroll_adjustments = foo_scroll_area_set_scroll_adjustments;
+
+    parent_class = g_type_class_peek_parent (class);
+
+    signals[VIEWPORT_CHANGED] =
+    g_signal_new ("viewport_changed",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (FooScrollAreaClass,
+                                   viewport_changed),
+                  NULL, NULL,
+                  foo_marshal_VOID__BOXED_BOXED,
+                  G_TYPE_NONE, 2,
+                  GDK_TYPE_RECTANGLE,
+                  GDK_TYPE_RECTANGLE);
+
+    signals[PAINT] =
+    g_signal_new ("paint",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (FooScrollAreaClass,
+                                   paint),
+                  NULL, NULL,
+                  foo_marshal_VOID__POINTER_BOXED_POINTER,
+                  G_TYPE_NONE, 3,
+                  G_TYPE_POINTER,
+                  GDK_TYPE_RECTANGLE,
+                  G_TYPE_POINTER);
+
+    widget_class->set_scroll_adjustments_signal =
+    g_signal_new ("set_scroll_adjustments",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (FooScrollAreaClass,
+                                   set_scroll_adjustments),
+                  NULL, NULL,
+                  foo_marshal_VOID__OBJECT_OBJECT,
+                  G_TYPE_NONE, 2,
+                  GTK_TYPE_ADJUSTMENT,
+                  GTK_TYPE_ADJUSTMENT);
+}
+
+static GtkAdjustment *
+new_adjustment (void)
+{
+    return GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+}
+
+static void
+foo_scroll_area_init (FooScrollArea *scroll_area)
+{
+    GtkWidget *widget;
+
+    widget = GTK_WIDGET (scroll_area);
+
+    gtk_widget_set_has_window (widget, FALSE);
+    gtk_widget_set_redraw_on_allocate (widget, FALSE);
+
+    scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1);
+    scroll_area->priv->width = 0;
+    scroll_area->priv->height = 0;
+    scroll_area->priv->hadj = g_object_ref_sink (new_adjustment());
+    scroll_area->priv->vadj = g_object_ref_sink (new_adjustment());
+    scroll_area->priv->x_offset = 0.0;
+    scroll_area->priv->y_offset = 0.0;
+    scroll_area->priv->min_width = -1;
+    scroll_area->priv->min_height = -1;
+    scroll_area->priv->auto_scroll_info = NULL;
+    scroll_area->priv->input_regions = g_ptr_array_new ();
+    scroll_area->priv->pixmap = NULL;
+    scroll_area->priv->update_region = gdk_region_new ();
+
+    gtk_widget_set_double_buffered (widget, FALSE);
+}
+
+static void
+translate_cairo_device (cairo_t       *cr,
+                        int            x_offset,
+                        int            y_offset)
+{
+    cairo_surface_t *surface = cairo_get_target (cr);
+    double dev_x;
+    double dev_y;
+
+    cairo_surface_get_device_offset (surface, &dev_x, &dev_y);
+    dev_x += x_offset;
+    dev_y += y_offset;
+    cairo_surface_set_device_offset (surface, dev_x, dev_y);
+}
+
+typedef void (* PathForeachFunc) (double  *x,
+                                  double  *y,
+                                  gpointer data);
+
+static void
+path_foreach_point (cairo_path_t     *path,
+                    PathForeachFunc   func,
+                    gpointer          user_data)
+{
+    int i;
+
+    for (i = 0; i < path->num_data; i += path->data[i].header.length)
+    {
+        cairo_path_data_t *data = &(path->data[i]);
+
+        switch (data->header.type)
+        {
+            case CAIRO_PATH_MOVE_TO:
+            case CAIRO_PATH_LINE_TO:
+                func (&(data[1].point.x), &(data[1].point.y), user_data);
+                break;
+
+            case CAIRO_PATH_CURVE_TO:
+                func (&(data[1].point.x), &(data[1].point.y), user_data);
+                func (&(data[2].point.x), &(data[2].point.y), user_data);
+                func (&(data[3].point.x), &(data[3].point.y), user_data);
+                break;
+
+            case CAIRO_PATH_CLOSE_PATH:
+                break;
+        }
+    }
+}
+
+typedef struct
+{
+    double x1, y1, x2, y2;
+} Box;
+
+static void
+input_path_free_list (InputPath *paths)
+{
+    if (!paths)
+        return;
+
+    input_path_free_list (paths->next);
+    cairo_path_destroy (paths->path);
+    g_free (paths);
+}
+
+static void
+input_region_free (InputRegion *region)
+{
+    input_path_free_list (region->paths);
+    gdk_region_destroy (region->region);
+
+    g_free (region);
+}
+
+static void
+get_viewport (FooScrollArea *scroll_area,
+              GdkRectangle  *viewport)
+{
+    GtkAllocation allocation;
+    GtkWidget *widget = GTK_WIDGET (scroll_area);
+
+    gtk_widget_get_allocation (widget, &allocation);
+
+    viewport->x = scroll_area->priv->x_offset;
+    viewport->y = scroll_area->priv->y_offset;
+    viewport->width = allocation.width;
+    viewport->height = allocation.height;
+}
+
+static void
+allocation_to_canvas (FooScrollArea *area,
+                      int           *x,
+                      int           *y)
+{
+    *x += area->priv->x_offset;
+    *y += area->priv->y_offset;
+}
+
+static void
+clear_exposed_input_region (FooScrollArea *area,
+                            GdkRegion *exposed) /* in canvas coordinates */
+{
+    guint i;
+    GdkRegion *viewport;
+    GdkRectangle allocation;
+
+    gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
+    allocation.x = 0;
+    allocation.y = 0;
+    allocation_to_canvas (area, &allocation.x, &allocation.y);
+    viewport = gdk_region_rectangle (&allocation);
+    gdk_region_subtract (viewport, exposed);
+
+    for (i = 0; i < area->priv->input_regions->len; ++i)
+    {
+        InputRegion *region = area->priv->input_regions->pdata[i];
+
+        gdk_region_intersect (region->region, viewport);
+
+        if (gdk_region_empty (region->region))
+        {
+            input_region_free (region);
+            g_ptr_array_remove_index_fast (area->priv->input_regions, i--);
+        }
+    }
+
+    gdk_region_destroy (viewport);
+}
+
+static void
+setup_background_cr (GdkWindow *window,
+                     cairo_t   *cr,
+                     int        x_offset,
+                     int        y_offset)
+{
+    GdkWindowObject *private = (GdkWindowObject *)window;
+
+    if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent)
+    {
+        x_offset += private->x;
+        y_offset += private->y;
+
+        setup_background_cr (GDK_WINDOW (private->parent), cr, x_offset, y_offset);
+    }
+    else if (private->bg_pixmap &&
+         private->bg_pixmap != GDK_PARENT_RELATIVE_BG &&
+         private->bg_pixmap != GDK_NO_BG)
+    {
+        gdk_cairo_set_source_pixmap (cr, private->bg_pixmap, -x_offset, -y_offset);
+    }
+    else
+    {
+        gdk_cairo_set_source_color (cr, &private->bg_color);
+    }
+}
+
+static void
+initialize_background (GtkWidget *widget,
+                       cairo_t   *cr)
+{
+    setup_background_cr (gtk_widget_get_window (widget), cr, 0, 0);
+
+    cairo_paint (cr);
+}
+
+static void
+clip_to_region (cairo_t *cr, GdkRegion *region)
+{
+    int n_rects;
+    GdkRectangle *rects;
+
+    gdk_region_get_rectangles (region, &rects, &n_rects);
+
+    cairo_new_path (cr);
+    while (n_rects--)
+    {
+        GdkRectangle *rect = &(rects[n_rects]);
+
+        cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+    }
+    cairo_clip (cr);
+
+    g_free (rects);
+}
+
+static void
+simple_draw_drawable (GdkDrawable *dst,
+                      GdkDrawable *src,
+                      int          src_x,
+                      int          src_y,
+                      int          dst_x,
+                      int          dst_y,
+                      int          width,
+                      int          height)
+{
+    GdkGC *gc = gdk_gc_new (dst);
+
+    gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height);
+
+    g_object_unref (gc);
+}
+
+static gboolean
+foo_scroll_area_expose (GtkWidget *widget,
+                        GdkEventExpose *expose)
+{
+    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+    cairo_t *cr;
+    GdkGC *gc;
+    GdkRectangle extents;
+    GdkWindow *window = gtk_widget_get_window (widget);
+    GdkRegion *region;
+    int x_offset, y_offset;
+    GtkAllocation widget_allocation;
+
+    /* I don't think expose can ever recurse for the same area */
+    g_assert (!scroll_area->priv->expose_region);
+
+    /* Note that this function can be called at a time
+     * where the adj->value is different from x_offset.
+     * Ie., the GtkScrolledWindow changed the adj->value
+     * without emitting the value_changed signal.
+     *
+     * Hence we must always use the value we got
+     * the last time the signal was emitted, ie.,
+     * priv->{x,y}_offset.
+     */
+
+    x_offset = scroll_area->priv->x_offset;
+    y_offset = scroll_area->priv->y_offset;
+
+    scroll_area->priv->expose_region = expose->region;
+
+    /* Setup input areas */
+    clear_exposed_input_region (scroll_area, scroll_area->priv->update_region);
+
+    scroll_area->priv->current_input = g_new0 (InputRegion, 1);
+    scroll_area->priv->current_input->region = gdk_region_copy (scroll_area->priv->update_region);
+    scroll_area->priv->current_input->paths = NULL;
+    g_ptr_array_add (scroll_area->priv->input_regions,
+                     scroll_area->priv->current_input);
+
+    region = scroll_area->priv->update_region;
+    scroll_area->priv->update_region = gdk_region_new ();
+
+    /* Create cairo context */
+    cr = gdk_cairo_create (scroll_area->priv->pixmap);
+    translate_cairo_device (cr, -x_offset, -y_offset);
+    clip_to_region (cr, region);
+    initialize_background (widget, cr);
+
+    /* Create regions */
+    gdk_region_get_clipbox (region, &extents);
+
+    g_signal_emit (widget, signals[PAINT], 0, cr, &extents, region);
+
+    /* Destroy stuff */
+    cairo_destroy (cr);
+
+    scroll_area->priv->expose_region = NULL;
+    scroll_area->priv->current_input = NULL;
+
+    /* Finally draw the backing pixmap */
+    gc = gdk_gc_new (window);
+
+    gdk_gc_set_clip_region (gc, expose->region);
+
+    gtk_widget_get_allocation (widget, &widget_allocation);
+    gdk_draw_drawable (window, gc, scroll_area->priv->pixmap,
+                       0, 0, widget_allocation.x, widget_allocation.y,
+                       widget_allocation.width, widget_allocation.height);
+
+    g_object_unref (gc);
+    gdk_region_destroy (region);
+
+    return TRUE;
+}
+
+void
+foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
+                              GdkRectangle  *viewport)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+    if (!viewport)
+        return;
+
+    get_viewport (scroll_area, viewport);
+}
+
+static void
+process_event (FooScrollArea           *scroll_area,
+               FooScrollAreaEventType   input_type,
+               int                      x,
+               int                      y);
+
+static void
+emit_viewport_changed (FooScrollArea *scroll_area,
+                       GdkRectangle  *new_viewport,
+                       GdkRectangle  *old_viewport)
+{
+    int px, py;
+    g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0,
+                   new_viewport, old_viewport);
+
+    gdk_window_get_pointer (scroll_area->priv->input_window, &px, &py, NULL);
+
+    process_event (scroll_area, FOO_MOTION, px, py);
+}
+
+static void
+clamp_adjustment (GtkAdjustment *adj)
+{
+    if (gtk_adjustment_get_upper (adj) >= gtk_adjustment_get_page_size (adj))
+        gtk_adjustment_set_value (adj, CLAMP (gtk_adjustment_get_value (adj), 0.0,
+                                              gtk_adjustment_get_upper (adj)
+                                               - gtk_adjustment_get_page_size (adj)));
+    else
+        gtk_adjustment_set_value (adj, 0.0);
+
+    gtk_adjustment_changed (adj);
+}
+
+static gboolean
+set_adjustment_values (FooScrollArea *scroll_area)
+{
+    GtkAllocation allocation;
+
+    GtkAdjustment *hadj = scroll_area->priv->hadj;
+    GtkAdjustment *vadj = scroll_area->priv->vadj;
+
+    /* Horizontal */
+    gtk_widget_get_allocation (GTK_WIDGET (scroll_area), &allocation);
+    g_object_freeze_notify (G_OBJECT (hadj));
+    gtk_adjustment_set_page_size (hadj, allocation.width);
+    gtk_adjustment_set_step_increment (hadj, 0.1 * allocation.width);
+    gtk_adjustment_set_page_increment (hadj, 0.9 * allocation.width);
+    gtk_adjustment_set_lower (hadj, 0.0);
+    gtk_adjustment_set_upper (hadj, scroll_area->priv->width);
+    g_object_thaw_notify (G_OBJECT (hadj));
+
+    /* Vertical */
+    g_object_freeze_notify (G_OBJECT (vadj));
+    gtk_adjustment_set_page_size (vadj, allocation.height);
+    gtk_adjustment_set_step_increment (vadj, 0.1 * allocation.height);
+    gtk_adjustment_set_page_increment (vadj, 0.9 * allocation.height);
+    gtk_adjustment_set_lower (vadj, 0.0);
+    gtk_adjustment_set_upper (vadj, scroll_area->priv->height);
+    g_object_thaw_notify (G_OBJECT (vadj));
+
+    clamp_adjustment (hadj);
+    clamp_adjustment (vadj);
+
+    return TRUE;
+}
+
+static void
+foo_scroll_area_realize (GtkWidget *widget)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+    GdkWindowAttr attributes;
+    GtkAllocation widget_allocation;
+    GdkWindow *window;
+    gint attributes_mask;
+
+    gtk_widget_get_allocation (widget, &widget_allocation);
+    gtk_widget_set_realized (widget, TRUE);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget_allocation.x;
+    attributes.y = widget_allocation.y;
+    attributes.width = widget_allocation.width;
+    attributes.height = widget_allocation.height;
+    attributes.wclass = GDK_INPUT_ONLY;
+    attributes.event_mask = gtk_widget_get_events (widget);
+    attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                              GDK_BUTTON_RELEASE_MASK |
+                              GDK_BUTTON1_MOTION_MASK |
+                              GDK_BUTTON2_MOTION_MASK |
+                              GDK_BUTTON3_MOTION_MASK |
+                              GDK_POINTER_MOTION_MASK |
+                              GDK_ENTER_NOTIFY_MASK |
+                              GDK_LEAVE_NOTIFY_MASK);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+    window = gtk_widget_get_parent_window (widget);
+    gtk_widget_set_window (widget, window);
+    g_object_ref (window);
+
+    area->priv->input_window = gdk_window_new (window,
+                                               &attributes, attributes_mask);
+
+    area->priv->pixmap = gdk_pixmap_new (window,
+                                         widget_allocation.width,
+                                         widget_allocation.height,
+                                         -1);
+
+    gdk_window_set_user_data (area->priv->input_window, area);
+
+    gtk_widget_style_attach (widget);
+}
+
+static void
+foo_scroll_area_unrealize (GtkWidget *widget)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    if (area->priv->input_window)
+    {
+        gdk_window_set_user_data (area->priv->input_window, NULL);
+        gdk_window_destroy (area->priv->input_window);
+        area->priv->input_window = NULL;
+    }
+
+    GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+}
+
+static GdkPixmap *
+create_new_pixmap (GtkWidget *widget,
+                   GdkPixmap *old)
+{
+    GtkAllocation widget_allocation;
+    GdkPixmap *new;
+
+    gtk_widget_get_allocation (widget, &widget_allocation);
+
+    new = gdk_pixmap_new (gtk_widget_get_window (widget),
+                          widget_allocation.width,
+                          widget_allocation.height,
+                          -1);
+
+    /* Unfortunately we don't know in which direction we were resized,
+     * so we just assume we were dragged from the south-east corner.
+     *
+     * Although, maybe we could get the root coordinates of the input-window?
+     * That might just work, actually. We need to make sure marco uses
+     * static gravity for the window before this will be useful.
+     */
+    simple_draw_drawable (new, old, 0, 0, 0, 0, -1, -1);
+
+    return new;
+}
+
+static void
+allocation_to_canvas_region (FooScrollArea *area,
+                 GdkRegion *region)
+{
+    gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset);
+}
+
+static void
+foo_scroll_area_size_allocate (GtkWidget     *widget,
+                               GtkAllocation *allocation)
+{
+    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+    GdkRectangle new_viewport;
+    GdkRectangle old_viewport;
+    GdkRegion *old_allocation;
+    GdkRegion *invalid;
+    GtkAllocation widget_allocation;
+
+    get_viewport (scroll_area, &old_viewport);
+
+    gtk_widget_get_allocation (widget, &widget_allocation);
+    old_allocation = gdk_region_rectangle (&widget_allocation);
+    gdk_region_offset (old_allocation,
+                       -widget_allocation.x, -widget_allocation.y);
+    invalid = gdk_region_rectangle (allocation);
+    gdk_region_offset (invalid, -allocation->x, -allocation->y);
+    gdk_region_xor (invalid, old_allocation);
+    allocation_to_canvas_region (scroll_area, invalid);
+    foo_scroll_area_invalidate_region (scroll_area, invalid);
+    gdk_region_destroy (old_allocation);
+    gdk_region_destroy (invalid);
+
+    gtk_widget_set_allocation (widget, allocation);
+
+    if (scroll_area->priv->input_window)
+    {
+        GdkPixmap *new_pixmap;
+
+        gdk_window_move_resize (scroll_area->priv->input_window,
+                                allocation->x, allocation->y,
+                                allocation->width, allocation->height);
+
+        new_pixmap = create_new_pixmap (widget, scroll_area->priv->pixmap);
+        g_object_unref (scroll_area->priv->pixmap);
+        scroll_area->priv->pixmap = new_pixmap;
+    }
+
+    get_viewport (scroll_area, &new_viewport);
+
+    emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
+}
+
+static void
+emit_input (FooScrollArea          *scroll_area,
+            FooScrollAreaEventType type,
+            int                    x,
+            int                    y,
+            FooScrollAreaEventFunc func,
+            gpointer               data)
+{
+    FooScrollAreaEvent event;
+
+    if (!func)
+        return;
+
+    if (type != FOO_MOTION)
+        emit_input (scroll_area, FOO_MOTION, x, y, func, data);
+
+    event.type = type;
+    event.x = x;
+    event.y = y;
+
+    func (scroll_area, &event, data);
+}
+
+static void
+process_event (FooScrollArea           *scroll_area,
+               FooScrollAreaEventType   input_type,
+               int                      x,
+               int                      y)
+{
+    GtkWidget *widget = GTK_WIDGET (scroll_area);
+    guint i;
+
+    allocation_to_canvas (scroll_area, &x, &y);
+
+    if (scroll_area->priv->grabbed)
+    {
+        emit_input (scroll_area, input_type, x, y,
+                scroll_area->priv->grab_func,
+                scroll_area->priv->grab_data);
+        return;
+    }
+
+    for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
+    {
+        InputRegion *region = scroll_area->priv->input_regions->pdata[i];
+
+        if (gdk_region_point_in (region->region, x, y))
+        {
+            InputPath *path;
+
+            path = region->paths;
+            while (path)
+            {
+                cairo_t *cr;
+                gboolean inside;
+
+                cr = gdk_cairo_create (gtk_widget_get_window (widget));
+                cairo_set_fill_rule (cr, path->fill_rule);
+                cairo_set_line_width (cr, path->line_width);
+                cairo_append_path (cr, path->path);
+
+                if (path->is_stroke)
+                    inside = cairo_in_stroke (cr, x, y);
+                else
+                    inside = cairo_in_fill (cr, x, y);
+
+                cairo_destroy (cr);
+
+                if (inside)
+                {
+                    emit_input (scroll_area, input_type,
+                                x, y,
+                                path->func,
+                                path->data);
+                    return;
+                }
+
+                path = path->next;
+            }
+
+            /* Since the regions are all disjoint, no other region
+             * can match. Of course we could be clever and try and
+             * sort the regions, but so far I have been unable to
+             * make this loop show up on a profile.
+             */
+            return;
+        }
+    }
+}
+
+static void
+process_gdk_event (FooScrollArea *scroll_area,
+                   int            x,
+                   int            y,
+                   GdkEvent      *event)
+{
+    FooScrollAreaEventType input_type;
+
+    if (event->type == GDK_BUTTON_PRESS)
+        input_type = FOO_BUTTON_PRESS;
+    else if (event->type == GDK_BUTTON_RELEASE)
+        input_type = FOO_BUTTON_RELEASE;
+    else if (event->type == GDK_MOTION_NOTIFY)
+        input_type = FOO_MOTION;
+    else
+        return;
+
+    process_event (scroll_area, input_type, x, y);
+}
+
+static gboolean
+foo_scroll_area_button_press (GtkWidget *widget,
+                              GdkEventButton *event)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+
+    return TRUE;
+}
+
+static gboolean
+foo_scroll_area_button_release (GtkWidget      *widget,
+                                GdkEventButton *event)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+
+    return FALSE;
+}
+
+static gboolean
+foo_scroll_area_motion (GtkWidget      *widget,
+                        GdkEventMotion *event)
+{
+    FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+    process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+    return TRUE;
+}
+
+void
+foo_scroll_area_set_size_fixed_y (FooScrollArea    *scroll_area,
+                                  int               width,
+                                  int               height,
+                                  int               old_y,
+                                  int               new_y)
+{
+    scroll_area->priv->width = width;
+    scroll_area->priv->height = height;
+
+    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
+    gtk_adjustment_set_value (scroll_area->priv->vadj, new_y);
+
+    set_adjustment_values (scroll_area);
+    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
+}
+
+void
+foo_scroll_area_set_size (FooScrollArea    *scroll_area,
+                          int               width,
+                          int               height)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+    /* FIXME: Default scroll algorithm should probably be to
+     * keep the same *area* outside the screen as before.
+     *
+     * For wrapper widgets that will do something roughly
+     * right. For widgets that don't change size, it
+     * will do the right thing. Except for idle-layouting
+     * widgets.
+     *
+     * Maybe there should be some generic support for those
+     * widgets. Can that even be done?
+     *
+     * Should we have a version of this function using
+     * fixed points?
+     */
+
+    scroll_area->priv->width = width;
+    scroll_area->priv->height = height;
+
+    set_adjustment_values (scroll_area);
+}
+
+static void
+foo_scroll_area_size_request (GtkWidget      *widget,
+                              GtkRequisition *requisition)
+{
+    FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+
+    requisition->width = scroll_area->priv->min_width;
+    requisition->height = scroll_area->priv->min_height;
+}
+
+static void
+foo_scroll_area_scroll (FooScrollArea *area,
+                        gint dx,
+                        gint dy)
+{
+    GdkRectangle allocation;
+    GdkRectangle src_area;
+    GdkRectangle move_area;
+    GdkRegion *invalid_region;
+
+    gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
+    allocation.x = 0;
+    allocation.y = 0;
+
+    src_area = allocation;
+    src_area.x -= dx;
+    src_area.y -= dy;
+
+    invalid_region = gdk_region_rectangle (&allocation);
+
+    if (gdk_rectangle_intersect (&allocation, &src_area, &move_area))
+    {
+        GdkRegion *move_region;
+
+        simple_draw_drawable (area->priv->pixmap, area->priv->pixmap,
+                              move_area.x, move_area.y,
+                              move_area.x + dx, move_area.y + dy,
+                              move_area.width, move_area.height);
+
+        gtk_widget_queue_draw (GTK_WIDGET (area));
+
+        move_region = gdk_region_rectangle (&move_area);
+        gdk_region_offset (move_region, dx, dy);
+        gdk_region_subtract (invalid_region, move_region);
+        gdk_region_destroy (move_region);
+    }
+
+    allocation_to_canvas_region (area, invalid_region);
+
+    foo_scroll_area_invalidate_region (area, invalid_region);
+
+    gdk_region_destroy (invalid_region);
+}
+
+static void
+foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
+                                  FooScrollArea *scroll_area)
+{
+    GtkWidget *widget = GTK_WIDGET (scroll_area);
+    gint dx = 0;
+    gint dy = 0;
+    GdkRectangle old_viewport, new_viewport;
+
+    get_viewport (scroll_area, &old_viewport);
+
+    if (adj == scroll_area->priv->hadj)
+    {
+        /* FIXME: do we treat the offset as int or double, and,
+         * if int, how do we round?
+         */
+        dx = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->x_offset;
+        scroll_area->priv->x_offset = gtk_adjustment_get_value (adj);
+    }
+    else if (adj == scroll_area->priv->vadj)
+    {
+        dy = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->y_offset;
+        scroll_area->priv->y_offset = gtk_adjustment_get_value (adj);
+    }
+    else
+    {
+        g_assert_not_reached ();
+    }
+
+    if (gtk_widget_get_realized (widget))
+    {
+        foo_scroll_area_scroll (scroll_area, -dx, -dy);
+    }
+
+    get_viewport (scroll_area, &new_viewport);
+
+    emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
+}
+
+static void
+set_one_adjustment (FooScrollArea *scroll_area,
+                    GtkAdjustment *adjustment,
+                    GtkAdjustment **location)
+{
+    g_return_if_fail (location != NULL);
+
+    if (adjustment == *location)
+        return;
+
+    if (!adjustment)
+        adjustment = new_adjustment ();
+
+    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+    if (*location)
+    {
+        g_signal_handlers_disconnect_by_func (
+            *location, foo_scrollbar_adjustment_changed, scroll_area);
+
+        g_object_unref (*location);
+    }
+
+    *location = adjustment;
+
+    g_object_ref_sink (*location);
+
+    g_signal_connect (*location, "value_changed",
+                      G_CALLBACK (foo_scrollbar_adjustment_changed),
+                      scroll_area);
+}
+
+static void
+foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+                                        GtkAdjustment *hadjustment,
+                                        GtkAdjustment *vadjustment)
+{
+    set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj);
+    set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj);
+
+    set_adjustment_values (scroll_area);
+}
+
+FooScrollArea *
+foo_scroll_area_new (void)
+{
+    return g_object_new (FOO_TYPE_SCROLL_AREA, NULL);
+}
+
+void
+foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
+                              int            min_width,
+                              int            min_height)
+{
+    scroll_area->priv->min_width = min_width;
+    scroll_area->priv->min_height = min_height;
+
+    /* FIXME: think through invalidation.
+     *
+     * Goals: - no repainting everything on size_allocate(),
+     *        - make sure input boxes are invalidated when
+     *          needed
+     */
+    gtk_widget_queue_resize (GTK_WIDGET (scroll_area));
+}
+
+static void
+user_to_device (double *x, double *y,
+                gpointer data)
+{
+    cairo_t *cr = data;
+
+    cairo_user_to_device (cr, x, y);
+}
+
+static InputPath *
+make_path (FooScrollArea         *area,
+           cairo_t               *cr,
+           gboolean               is_stroke,
+           FooScrollAreaEventFunc func,
+           gpointer               data)
+{
+    InputPath *path = g_new0 (InputPath, 1);
+
+    path->is_stroke = is_stroke;
+    path->fill_rule = cairo_get_fill_rule (cr);
+    path->line_width = cairo_get_line_width (cr);
+    path->path = cairo_copy_path (cr);
+    path_foreach_point (path->path, user_to_device, cr);
+    path->func = func;
+    path->data = data;
+    path->next = area->priv->current_input->paths;
+    area->priv->current_input->paths = path;
+    return path;
+}
+
+/* FIXME: we probably really want a
+ *
+ *  foo_scroll_area_add_input_from_fill (area, cr, ...);
+ * and
+ *      foo_scroll_area_add_input_from_stroke (area, cr, ...);
+ * as well.
+ */
+void
+foo_scroll_area_add_input_from_fill (FooScrollArea           *scroll_area,
+                                     cairo_t                 *cr,
+                                     FooScrollAreaEventFunc   func,
+                                     gpointer                 data)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+    g_return_if_fail (cr != NULL);
+    g_return_if_fail (scroll_area->priv->current_input);
+
+    make_path (scroll_area, cr, FALSE, func, data);
+}
+
+void
+foo_scroll_area_add_input_from_stroke (FooScrollArea           *scroll_area,
+                                       cairo_t                  *cr,
+                                       FooScrollAreaEventFunc   func,
+                                       gpointer                 data)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+    g_return_if_fail (cr != NULL);
+    g_return_if_fail (scroll_area->priv->current_input);
+
+    make_path (scroll_area, cr, TRUE, func, data);
+}
+
+void
+foo_scroll_area_invalidate (FooScrollArea *scroll_area)
+{
+    GtkAllocation allocation;
+    GtkWidget *widget = GTK_WIDGET (scroll_area);
+
+    gtk_widget_get_allocation (widget, &allocation);
+    foo_scroll_area_invalidate_rect (scroll_area,
+                     scroll_area->priv->x_offset, scroll_area->priv->y_offset,
+                     allocation.width,
+                     allocation.height);
+}
+
+static void
+canvas_to_window (FooScrollArea *area,
+                  GdkRegion     *region)
+{
+    GtkAllocation allocation;
+    GtkWidget *widget = GTK_WIDGET (area);
+
+    gtk_widget_get_allocation (widget, &allocation);
+    gdk_region_offset (region,
+               -area->priv->x_offset + allocation.x,
+               -area->priv->y_offset + allocation.y);
+}
+
+static void
+window_to_canvas (FooScrollArea *area,
+                  GdkRegion *region)
+{
+    GtkAllocation allocation;
+    GtkWidget *widget = GTK_WIDGET (area);
+
+    gtk_widget_get_allocation (widget, &allocation);
+    gdk_region_offset (region,
+               area->priv->x_offset - allocation.x,
+               area->priv->y_offset - allocation.y);
+}
+
+void
+foo_scroll_area_invalidate_region (FooScrollArea *area,
+                                   GdkRegion     *region)
+{
+    GtkWidget *widget;
+
+    g_return_if_fail (FOO_IS_SCROLL_AREA (area));
+
+    widget = GTK_WIDGET (area);
+
+    gdk_region_union (area->priv->update_region, region);
+
+    if (gtk_widget_get_realized (widget))
+    {
+        canvas_to_window (area, region);
+
+        gdk_window_invalidate_region (gtk_widget_get_window (widget),
+                                      region, TRUE);
+
+        window_to_canvas (area, region);
+    }
+}
+
+void
+foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
+                                 int            x,
+                                 int            y,
+                                 int            width,
+                                 int            height)
+{
+    GdkRectangle rect = { x, y, width, height };
+    GdkRegion *region;
+
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+    region = gdk_region_rectangle (&rect);
+
+    foo_scroll_area_invalidate_region (scroll_area, region);
+
+    gdk_region_destroy (region);
+}
+
+void
+foo_scroll_area_begin_grab (FooScrollArea *scroll_area,
+                            FooScrollAreaEventFunc func,
+                            gpointer       input_data)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+    g_return_if_fail (!scroll_area->priv->grabbed);
+
+    scroll_area->priv->grabbed = TRUE;
+    scroll_area->priv->grab_func = func;
+    scroll_area->priv->grab_data = input_data;
+
+    /* FIXME: we should probably take a server grab */
+    /* Also, maybe there should be support for setting the grab cursor */
+}
+
+void
+foo_scroll_area_end_grab (FooScrollArea *scroll_area)
+{
+    g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+    scroll_area->priv->grabbed = FALSE;
+    scroll_area->priv->grab_func = NULL;
+    scroll_area->priv->grab_data = NULL;
+}
+
+gboolean
+foo_scroll_area_is_grabbed (FooScrollArea *scroll_area)
+{
+    return scroll_area->priv->grabbed;
+}
+
+void
+foo_scroll_area_set_viewport_pos (FooScrollArea  *scroll_area,
+                                  int             x,
+                                  int             y)
+{
+    g_object_freeze_notify (G_OBJECT (scroll_area->priv->hadj));
+    g_object_freeze_notify (G_OBJECT (scroll_area->priv->vadj));
+    gtk_adjustment_set_value (scroll_area->priv->hadj, x);
+    gtk_adjustment_set_value (scroll_area->priv->vadj, y);
+
+    set_adjustment_values (scroll_area);
+    g_object_thaw_notify (G_OBJECT (scroll_area->priv->hadj));
+    g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj));
+}
+
+static gboolean
+rect_contains (const GdkRectangle *rect, int x, int y)
+{
+    return (x >= rect->x        &&
+            y >= rect->y        &&
+            x  < rect->x + rect->width  &&
+            y  < rect->y + rect->height);
+}
+
+static void
+stop_scrolling (FooScrollArea *area)
+{
+    if (area->priv->auto_scroll_info)
+    {
+        g_source_remove (area->priv->auto_scroll_info->timeout_id);
+        g_timer_destroy (area->priv->auto_scroll_info->timer);
+        g_free (area->priv->auto_scroll_info);
+
+        area->priv->auto_scroll_info = NULL;
+    }
+}
+
+static gboolean
+scroll_idle (gpointer data)
+{
+    GdkRectangle viewport, new_viewport;
+    FooScrollArea *area = data;
+    AutoScrollInfo *info = area->priv->auto_scroll_info;
+    int new_x, new_y;
+    double elapsed;
+
+    get_viewport (area, &viewport);
+
+    elapsed = g_timer_elapsed (info->timer, NULL);
+
+    info->res_x = elapsed * info->dx / 0.2;
+    info->res_y = elapsed * info->dy / 0.2;
+
+    new_x = viewport.x + info->res_x;
+    new_y = viewport.y + info->res_y;
+
+    foo_scroll_area_set_viewport_pos (area, new_x, new_y);
+
+    get_viewport (area, &new_viewport);
+
+    if (viewport.x == new_viewport.x        &&
+    viewport.y == new_viewport.y        &&
+    (info->res_x > 1.0          ||
+     info->res_y > 1.0          ||
+     info->res_x < -1.0         ||
+     info->res_y < -1.0))
+    {
+        stop_scrolling (area);
+
+        /* stop scrolling if it didn't have an effect */
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+ensure_scrolling (FooScrollArea *area,
+                  int            dx,
+                  int            dy)
+{
+    if (!area->priv->auto_scroll_info)
+    {
+        area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1);
+        area->priv->auto_scroll_info->timeout_id =
+            g_idle_add (scroll_idle, area);
+        area->priv->auto_scroll_info->timer = g_timer_new ();
+    }
+
+    area->priv->auto_scroll_info->dx = dx;
+    area->priv->auto_scroll_info->dy = dy;
+}
+
+void
+foo_scroll_area_auto_scroll (FooScrollArea      *scroll_area,
+                             FooScrollAreaEvent *event)
+{
+    GdkRectangle viewport;
+
+    get_viewport (scroll_area, &viewport);
+
+    if (rect_contains (&viewport, event->x, event->y))
+    {
+        stop_scrolling (scroll_area);
+    }
+    else
+    {
+        int dx, dy;
+
+        dx = dy = 0;
+
+        if (event->y < viewport.y)
+        {
+            dy = event->y - viewport.y;
+            dy = MIN (dy + 2, 0);
+        }
+        else if (event->y >= viewport.y + viewport.height)
+        {
+            dy = event->y - (viewport.y + viewport.height - 1);
+            dy = MAX (dy - 2, 0);
+        }
+
+        if (event->x < viewport.x)
+        {
+            dx = event->x - viewport.x;
+            dx = MIN (dx + 2, 0);
+        }
+        else if (event->x >= viewport.x + viewport.width)
+        {
+            dx = event->x - (viewport.x + viewport.width - 1);
+            dx = MAX (dx - 2, 0);
+        }
+
+        ensure_scrolling (scroll_area, dx, dy);
+    }
+}
+
+void
+foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area)
+{
+    /* noop for now */
+}
+
+void
+foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area)
+{
+    stop_scrolling (scroll_area);
+}
diff --git a/dialogs/display-settings/scrollarea.h b/dialogs/display-settings/scrollarea.h
new file mode 100644
index 0000000..dc8bb0d
--- /dev/null
+++ b/dialogs/display-settings/scrollarea.h
@@ -0,0 +1,137 @@
+/* Copyright 2006, 2007, 2008, Soren Sandmann <sandmann at daimi.au.dk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#define FOO_TYPE_SCROLL_AREA            (foo_scroll_area_get_type ())
+#define FOO_SCROLL_AREA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_SCROLL_AREA, FooScrollArea))
+#define FOO_SCROLL_AREA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  FOO_TYPE_SCROLL_AREA, FooScrollAreaClass))
+#define FOO_IS_SCROLL_AREA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_SCROLL_AREA))
+#define FOO_IS_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  FOO_TYPE_SCROLL_AREA))
+#define FOO_SCROLL_AREA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  FOO_TYPE_SCROLL_AREA, FooScrollAreaClass))
+
+typedef struct FooScrollArea        FooScrollArea;
+typedef struct FooScrollAreaClass   FooScrollAreaClass;
+typedef struct FooScrollAreaPrivate FooScrollAreaPrivate;
+typedef struct FooScrollAreaEvent   FooScrollAreaEvent;
+
+typedef enum
+{
+    FOO_BUTTON_PRESS,
+    FOO_BUTTON_RELEASE,
+    FOO_MOTION
+} FooScrollAreaEventType;
+
+struct FooScrollAreaEvent
+{
+    FooScrollAreaEventType  type;
+    int                     x;
+    int                     y;
+};
+
+typedef void (* FooScrollAreaEventFunc) (FooScrollArea      *area,
+                                         FooScrollAreaEvent *event,
+                                         gpointer            data);
+
+struct FooScrollArea
+{
+    GtkContainer parent_instance;
+
+    FooScrollAreaPrivate *priv;
+};
+
+struct FooScrollAreaClass
+{
+    GtkContainerClass parent_class;
+
+    void (*set_scroll_adjustments) (FooScrollArea *scroll_area,
+                                    GtkAdjustment *hadjustment,
+                                    GtkAdjustment *vadjustment);
+
+    void (*viewport_changed) (FooScrollArea *scroll_area,
+                              GdkRectangle  *old_viewport,
+                              GdkRectangle  *new_viewport);
+
+    void (*paint) (FooScrollArea *scroll_area,
+                   cairo_t       *cr,
+                   GdkRectangle  *extents,
+                   GdkRegion     *region);
+};
+
+GType foo_scroll_area_get_type (void);
+
+FooScrollArea *foo_scroll_area_new (void);
+
+/* Set the requisition for the widget. */
+void          foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
+                                            int            min_width,
+                                            int            min_height);
+
+/* Set how much of the canvas can be scrolled into view */
+void          foo_scroll_area_set_size (FooScrollArea          *scroll_area,
+                                        int                     width,
+                                        int                     height);
+
+void          foo_scroll_area_set_size_fixed_y (FooScrollArea  *scroll_area,
+                                                int             width,
+                                                int             height,
+                                                int             old_y,
+                                                int             new_y);
+
+void          foo_scroll_area_set_viewport_pos (FooScrollArea  *scroll_area,
+                                                int             x,
+                                                int             y);
+
+void          foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
+                                            GdkRectangle  *viewport);
+
+void          foo_scroll_area_add_input_from_stroke (FooScrollArea           *scroll_area,
+                                                     cairo_t                 *cr,
+                                                     FooScrollAreaEventFunc   func,
+                                                     gpointer                 data);
+
+void          foo_scroll_area_add_input_from_fill (FooScrollArea         *scroll_area,
+                                                   cairo_t                *cr,
+                                                   FooScrollAreaEventFunc  func,
+                                                   gpointer                data);
+
+void          foo_scroll_area_invalidate_region (FooScrollArea *area,
+                                                 GdkRegion     *region);
+
+void          foo_scroll_area_invalidate (FooScrollArea *scroll_area);
+
+void          foo_scroll_area_invalidate_rect (FooScrollArea   *scroll_area,
+                                               int              x,
+                                               int              y,
+                                               int              width,
+                                               int              height);
+
+void          foo_scroll_area_begin_grab (FooScrollArea          *scroll_area,
+                                          FooScrollAreaEventFunc  func,
+                                          gpointer                input_data);
+
+void          foo_scroll_area_end_grab (FooScrollArea *scroll_area);
+
+gboolean      foo_scroll_area_is_grabbed (FooScrollArea *scroll_area);
+
+void          foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area);
+
+void          foo_scroll_area_auto_scroll (FooScrollArea      *scroll_area,
+                                           FooScrollAreaEvent *event);
+
+void          foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area);
diff --git a/dialogs/display-settings/xfce-randr.c b/dialogs/display-settings/xfce-randr.c
index c114824..1b3573f 100644
--- a/dialogs/display-settings/xfce-randr.c
+++ b/dialogs/display-settings/xfce-randr.c
@@ -36,17 +36,6 @@
 
 
 
-typedef struct _XfceOutputPosition XfceOutputPosition;
-
-
-
-struct _XfceOutputPosition
-{
-    gint x;
-    gint y;
-};
-
-
 
 struct _XfceRandrPrivate
 {
@@ -59,13 +48,13 @@ struct _XfceRandrPrivate
     /* cache for the output/mode info */
     XRROutputInfo      **output_info;
     XfceRRMode         **modes;
-    XfceOutputPosition  *position;
 };
 
 
 
 static gchar *xfce_randr_friendly_name (XfceRandr *randr,
-                                        guint      output);
+                                        guint      output,
+                                        guint      output_rr_id);
 
 
 
@@ -140,111 +129,39 @@ xfce_randr_guess_relations (XfceRandr *randr)
     guint n, m;
 
     /* walk the connected outputs */
-    for (n = 0; n < randr->noutput; ++n)
+    for (n=0; n < randr->noutput; ++n)
     {
         /* ignore relations for inactive outputs */
         if (randr->mode[n] == None)
             continue;
 
-        for (m = 0; m < randr->noutput; ++m)
+        for (m=0; m < randr->noutput; ++m)
         {
             /* additionally ignore itself */
             if (randr->mode[m] == None || m == n)
                 continue;
 
-            /* horizontal scale */
-            if (randr->priv->position[n].x == randr->priv->position[m].x)
+            if (randr->position[n].x == randr->position[m].x &&
+                randr->position[n].y == randr->position[m].y)
             {
-                if (randr->priv->position[n].y == randr->priv->position[m].y)
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_MIRROR;
-                else if (randr->priv->position[n].y > randr->priv->position[m].y)
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_DOWN;
-                else
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_UP;
-
-                randr->related_to[n] = m;
-                break;
-            }
-
-            /* vertical scale */
-            if (randr->priv->position[n].y == randr->priv->position[m].y)
-            {
-                if (randr->priv->position[n].x == randr->priv->position[m].x)
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_MIRROR;
-                else if (randr->priv->position[n].x > randr->priv->position[m].x)
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_RIGHT;
-                else
-                    randr->relation[n] = XFCE_RANDR_PLACEMENT_LEFT;
-
-                randr->related_to[n] = m;
-                break;
+                randr->mirrored[n] = TRUE;
             }
         }
     }
 }
 
 
-
-static void
-xfce_randr_update_positions (XfceRandr *randr,
-                             guint      output)
-{
-    const XfceRRMode *cmode, *rmode;
-    guint             rel;
-    gint              x, y;
-
-    rel = randr->related_to[output];
-    /* ignore relations for inactive outputs */
-    if (randr->mode[output] == None || randr->mode[rel] == None)
-        return;
-
-    /* modes of the two related outputs */
-    cmode = xfce_randr_find_mode_by_id (randr, output, randr->mode[output]);
-    rmode = xfce_randr_find_mode_by_id (randr, rel, randr->mode[rel]);
-
-    /* coordinates of the related_to output */
-    x = randr->priv->position[rel].x;
-    y = randr->priv->position[rel].y;
-
-    switch (randr->relation[output])
-    {
-        case XFCE_RANDR_PLACEMENT_LEFT:
-            randr->priv->position[output].x = x;
-            randr->priv->position[output].y = y;
-            randr->priv->position[rel].x = x + xfce_randr_mode_width (cmode, randr->rotation[output]);
-            break;
-        case XFCE_RANDR_PLACEMENT_RIGHT:
-            randr->priv->position[output].x = x + xfce_randr_mode_width (rmode, randr->rotation[rel]);
-            randr->priv->position[output].y = y;
-            break;
-        case XFCE_RANDR_PLACEMENT_UP:
-            randr->priv->position[output].x = x;
-            randr->priv->position[output].y = y;
-            randr->priv->position[rel].y = y + xfce_randr_mode_height (cmode, randr->rotation[output]);
-            break;
-        case XFCE_RANDR_PLACEMENT_DOWN:
-            randr->priv->position[output].x = x;
-            randr->priv->position[output].y = y + xfce_randr_mode_height (rmode, randr->rotation[rel]);
-            break;
-        default:
-            randr->priv->position[output].x = x;
-            randr->priv->position[output].y = y;
-            break;
-    }
-}
-
-
-
 static void
 xfce_randr_populate (XfceRandr *randr,
                      Display   *xdisplay,
                      GdkWindow *root_window)
 {
-    GPtrArray     *outputs;
-    XRROutputInfo *output_info;
-    XRRCrtcInfo   *crtc_info;
-    gint           n;
-    guint          m;
+    GPtrArray      *outputs;
+    XRROutputInfo  *output_info;
+    XRRCrtcInfo    *crtc_info;
+    gint            n;
+    guint           m, connected;
+    guint          *output_ids = NULL;
 
     g_return_if_fail (randr != NULL);
     g_return_if_fail (randr->priv != NULL);
@@ -252,8 +169,10 @@ xfce_randr_populate (XfceRandr *randr,
 
     /* prepare the temporary cache */
     outputs = g_ptr_array_new ();
+    output_ids = g_malloc0 (randr->priv->resources->noutput * sizeof (guint));
 
     /* walk the outputs */
+    connected = 0;
     for (n = 0; n < randr->priv->resources->noutput; ++n)
     {
         /* get the output info */
@@ -266,6 +185,11 @@ xfce_randr_populate (XfceRandr *randr,
             XRRFreeOutputInfo (output_info);
             continue;
         }
+        else
+        {
+            output_ids[connected] = n;
+            connected++;
+        }
 
         /* cache it */
         g_ptr_array_add (outputs, output_info);
@@ -278,11 +202,10 @@ xfce_randr_populate (XfceRandr *randr,
     /* allocate final space for the settings */
     randr->mode = g_new0 (RRMode, randr->noutput);
     randr->priv->modes = g_new0 (XfceRRMode *, randr->noutput);
-    randr->priv->position = g_new0 (XfceOutputPosition, randr->noutput);
+    randr->position = g_new0 (XfceOutputPosition, randr->noutput);
     randr->rotation = g_new0 (Rotation, randr->noutput);
     randr->rotations = g_new0 (Rotation, randr->noutput);
-    randr->relation = g_new0 (XfceOutputRelation, randr->noutput);
-    randr->related_to = g_new0 (guint, randr->noutput);
+    randr->mirrored = g_new0 (gboolean, randr->noutput);
     randr->status = g_new0 (XfceOutputStatus, randr->noutput);
     randr->friendly_name = g_new0 (gchar *, randr->noutput);
 
@@ -294,7 +217,7 @@ xfce_randr_populate (XfceRandr *randr,
 
 #ifdef HAS_RANDR_ONE_POINT_THREE
         /* find the primary screen if supported */
-        if (randr->priv->has_1_3 && XRRGetOutputPrimary (xdisplay, GDK_WINDOW_XID (root_window)) == randr->priv->resources->outputs[m])
+        if (randr->priv->has_1_3 && XRRGetOutputPrimary (xdisplay, GDK_WINDOW_XID (root_window)) == randr->priv->resources->outputs[output_ids[m]])
             randr->status[m] = XFCE_OUTPUT_STATUS_PRIMARY;
         else
 #endif
@@ -307,8 +230,8 @@ xfce_randr_populate (XfceRandr *randr,
             randr->mode[m] = crtc_info->mode;
             randr->rotation[m] = crtc_info->rotation;
             randr->rotations[m] = crtc_info->rotations;
-            randr->priv->position[m].x = crtc_info->x;
-            randr->priv->position[m].y = crtc_info->y;
+            randr->position[m].x = crtc_info->x;
+            randr->position[m].y = crtc_info->y;
             XRRFreeCrtcInfo (crtc_info);
         }
         else
@@ -320,11 +243,12 @@ xfce_randr_populate (XfceRandr *randr,
         }
 
         /* fill in the name used by the UI */
-        randr->friendly_name[m] = xfce_randr_friendly_name (randr, m);
+        randr->friendly_name[m] = xfce_randr_friendly_name (randr, m, output_ids[m]);
     }
-
-    /* calculate relations from positions */
+    /* populate mirrored details */
     xfce_randr_guess_relations (randr);
+
+    g_free (output_ids);
 }
 
 
@@ -408,9 +332,7 @@ xfce_randr_cleanup (XfceRandr *randr)
     g_free (randr->rotation);
     g_free (randr->rotations);
     g_free (randr->status);
-    g_free (randr->relation);
-    g_free (randr->related_to);
-    g_free (randr->priv->position);
+    g_free (randr->position);
     g_free (randr->priv->output_info);
 }
 
@@ -463,8 +385,7 @@ void
 xfce_randr_save_output (XfceRandr     *randr,
                         const gchar   *scheme,
                         XfconfChannel *channel,
-                        guint          output,
-                        gint           rel_changed)
+                        guint          output)
 {
     gchar             property[512];
     gchar            *str_value;
@@ -539,17 +460,13 @@ xfce_randr_save_output (XfceRandr     *randr,
                              randr->status[output] == XFCE_OUTPUT_STATUS_PRIMARY);
 #endif
 
-    /* update positions according to the current relations */
-    if (rel_changed)
-        xfce_randr_update_positions (randr, output);
-
     /* save the position */
     g_snprintf (property, sizeof (property), "/%s/%s/Position/X", scheme,
                 randr->priv->output_info[output]->name);
-    xfconf_channel_set_int (channel, property, MAX (randr->priv->position[output].x, 0));
+    xfconf_channel_set_int (channel, property, MAX (randr->position[output].x, 0));
     g_snprintf (property, sizeof (property), "/%s/%s/Position/Y", scheme,
                 randr->priv->output_info[output]->name);
-    xfconf_channel_set_int (channel, property, MAX (randr->priv->position[output].y, 0));
+    xfconf_channel_set_int (channel, property, MAX (randr->position[output].y, 0));
 }
 
 
@@ -612,12 +529,13 @@ xfce_randr_read_edid_data (Display  *xdisplay,
 
 static gchar *
 xfce_randr_friendly_name (XfceRandr *randr,
-                          guint      output)
+                          guint      output,
+                          guint      output_rr_id)
 {
-    Display     *xdisplay;
-    MonitorInfo *info = NULL;
-    guint8      *edid_data;
-    gchar       *friendly_name = NULL;
+    Display        *xdisplay;
+    MonitorInfo    *info = NULL;
+    guint8         *edid_data;
+    gchar          *friendly_name = NULL;
     const gchar *name = randr->priv->output_info[output]->name;
 
     /* special case, a laptop */
@@ -627,13 +545,13 @@ xfce_randr_friendly_name (XfceRandr *randr,
 
     /* otherwise, get the vendor & size */
     xdisplay = gdk_x11_display_get_xdisplay (randr->priv->display);
-    edid_data = xfce_randr_read_edid_data (xdisplay, randr->priv->resources->outputs[output]);
+    edid_data = xfce_randr_read_edid_data (xdisplay, randr->priv->resources->outputs[output_rr_id]);
 
     if (edid_data)
         info = decode_edid (edid_data);
 
     if (info)
-        friendly_name = make_display_name (info);
+        friendly_name = make_display_name (info, output);
 
     g_free (info);
     g_free (edid_data);
@@ -784,8 +702,8 @@ xfce_randr_get_positions (XfceRandr *randr,
     g_return_val_if_fail (randr != NULL && x != NULL && y != NULL, FALSE);
     g_return_val_if_fail (output < randr->noutput, FALSE);
 
-    *x = randr->priv->position[output].x;
-    *y = randr->priv->position[output].y;
+    *x = randr->position[output].x;
+    *y = randr->position[output].y;
     return TRUE;
 }
 
diff --git a/dialogs/display-settings/xfce-randr.h b/dialogs/display-settings/xfce-randr.h
index 2a5ba4c..7bf23f3 100644
--- a/dialogs/display-settings/xfce-randr.h
+++ b/dialogs/display-settings/xfce-randr.h
@@ -37,8 +37,9 @@
 typedef struct _XfceRandr          XfceRandr;
 typedef struct _XfceRandrPrivate   XfceRandrPrivate;
 typedef struct _XfceRRMode         XfceRRMode;
+typedef struct _XfceOutputInfo     XfceOutputInfo;
 typedef enum   _XfceOutputStatus   XfceOutputStatus;
-typedef enum   _XfceOutputRelation XfceOutputRelation;
+typedef struct _XfceOutputPosition XfceOutputPosition;
 
 enum _XfceOutputStatus
 {
@@ -46,15 +47,6 @@ enum _XfceOutputStatus
     XFCE_OUTPUT_STATUS_SECONDARY
 };
 
-enum _XfceOutputRelation
-{
-    XFCE_RANDR_PLACEMENT_MIRROR,
-    XFCE_RANDR_PLACEMENT_UP,
-    XFCE_RANDR_PLACEMENT_DOWN,
-    XFCE_RANDR_PLACEMENT_RIGHT,
-    XFCE_RANDR_PLACEMENT_LEFT
-};
-
 struct _XfceRRMode
 {
     RRMode  id;
@@ -63,6 +55,12 @@ struct _XfceRRMode
     gdouble rate;
 };
 
+struct _XfceOutputPosition
+{
+    gint x;
+    gint y;
+};
+
 struct _XfceRandr
 {
     /* number of connected outputs */
@@ -72,16 +70,43 @@ struct _XfceRandr
     RRMode              *mode;
     Rotation            *rotation;
     Rotation            *rotations;
-    XfceOutputRelation  *relation;
-    guint               *related_to;
+    XfceOutputPosition  *position;
     XfceOutputStatus    *status;
+    gboolean            *mirrored;
     gchar              **friendly_name;
 
     /* implementation details */
     XfceRandrPrivate    *priv;
 };
 
-
+struct _XfceOutputInfo
+{
+    /* Identifiers */
+    guint      id;
+    gchar     *display_name;
+
+    /* Status */
+    gboolean   on;
+    gboolean   connected;
+    gboolean   mirrored;
+
+    /* Position */
+    gint      x;
+    gint      y;
+
+    /* Dimensions */
+    guint      width;
+    guint      height;
+    guint      pref_width;
+    guint      pref_height;
+    Rotation   rotation;
+
+    /* Frequency */
+    gdouble    rate;
+
+    /* User Data (e.g. GrabInfo) */
+    gpointer   user_data;
+};
 
 XfceRandr        *xfce_randr_new             (GdkDisplay      *display,
                                               GError         **error);
@@ -93,8 +118,7 @@ void              xfce_randr_reload          (XfceRandr        *randr);
 void              xfce_randr_save_output     (XfceRandr        *randr,
                                               const gchar      *scheme,
                                               XfconfChannel    *channel,
-                                              guint             output,
-                                              gint              rel_changed);
+                                              guint             output);
 
 void              xfce_randr_apply           (XfceRandr        *randr,
                                               const gchar      *scheme,
diff --git a/xfsettingsd/debug.c b/xfsettingsd/debug.c
index e9653e8..1b26a6d 100644
--- a/xfsettingsd/debug.c
+++ b/xfsettingsd/debug.c
@@ -70,7 +70,7 @@ xfsettings_dbg_init (void)
 
 
 
-static void
+static void __attribute__((format (gnu_printf, 2,0)))
 xfsettings_dbg_print (XfsdDebugDomain  domain,
                       const gchar     *message,
                       va_list          args)

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list