[Xfce4-commits] <xfce4-settings:master> Refactor XfceDisplaysHelper to listen to screen changes

Simon Steinbeiss noreply at xfce.org
Wed Nov 7 19:50:02 CET 2012


Updating branch refs/heads/master
         to 7ef545b96688fd5797a3d422e50fa7be7c676af5 (commit)
       from cb8ab60dc89ab61ef62fd85b4f11c11026e79794 (commit)

commit 7ef545b96688fd5797a3d422e50fa7be7c676af5
Author: Lionel Le Folgoc <lionel at lefolgoc.net>
Date:   Tue Nov 6 22:58:51 2012 +0100

    Refactor XfceDisplaysHelper to listen to screen changes
    
    This will enable it to take some actions depending on the context, e.g.
    re-enable LVDS1 when VGA1 is disconnected, etc (to do for later).
    
    Signed-off-by: Simon Steinbeiss <simon.steinbeiss at elfenbeinturm.at>

 xfsettingsd/displays.c | 1190 +++++++++++++++++++++++++++---------------------
 1 files changed, 675 insertions(+), 515 deletions(-)

diff --git a/xfsettingsd/displays.c b/xfsettingsd/displays.c
index af6e6cd..597e0d9 100644
--- a/xfsettingsd/displays.c
+++ b/xfsettingsd/displays.c
@@ -47,12 +47,44 @@
 
 
 
-static void            xfce_displays_helper_channel_apply                  (XfceDisplaysHelper      *helper,
-                                                                            const gchar             *scheme);
-static void            xfce_displays_helper_channel_property_changed       (XfconfChannel           *channel,
-                                                                            const gchar             *property_name,
-                                                                            const GValue            *value,
-                                                                            XfceDisplaysHelper      *helper);
+/* wrappers to avoid querying too often */
+typedef struct _XfceRRCrtc XfceRRCrtc;
+
+
+
+static void             xfce_displays_helper_dispose                        (GObject                 *object);
+static void             xfce_displays_helper_finalize                       (GObject                 *object);
+static void             xfce_displays_helper_reload                         (XfceDisplaysHelper      *helper);
+static GdkFilterReturn  xfce_displays_helper_screen_on_event                (GdkXEvent               *xevent,
+                                                                             GdkEvent                *event,
+                                                                             gpointer                 data);
+static void             xfce_displays_helper_set_screen_size                (XfceDisplaysHelper      *helper);
+static gboolean         xfce_displays_helper_load_from_xfconf               (XfceDisplaysHelper      *helper,
+                                                                             const gchar             *scheme,
+                                                                             GHashTable              *saved_outputs,
+                                                                             RROutput                 output);
+static GPtrArray       *xfce_displays_helper_list_crtcs                     (XfceDisplaysHelper      *helper);
+static XfceRRCrtc      *xfce_displays_helper_find_crtc_by_id                (XfceDisplaysHelper      *helper,
+                                                                             RRCrtc                   id);
+static void             xfce_displays_helper_free_crtc                      (XfceRRCrtc              *crtc);
+static XfceRRCrtc      *xfce_displays_helper_find_usable_crtc               (XfceDisplaysHelper      *helper,
+                                                                             RROutput                 output);
+static void             xfce_displays_helper_normalize_crtc                 (XfceRRCrtc              *crtc,
+                                                                             XfceDisplaysHelper      *helper);
+static Status           xfce_displays_helper_disable_crtc                   (XfceDisplaysHelper      *helper,
+                                                                             RRCrtc                   crtc);
+static void             xfce_displays_helper_workaround_crtc_size           (XfceRRCrtc              *crtc,
+                                                                             XfceDisplaysHelper      *helper);
+static void             xfce_displays_helper_apply_crtc                     (XfceRRCrtc              *crtc,
+                                                                             XfceDisplaysHelper      *helper);
+static void             xfce_displays_helper_set_outputs                    (XfceRRCrtc              *crtc,
+                                                                             RROutput                 output);
+static void             xfce_displays_helper_channel_apply                  (XfceDisplaysHelper      *helper,
+                                                                             const gchar             *scheme);
+static void             xfce_displays_helper_channel_property_changed       (XfconfChannel           *channel,
+                                                                             const gchar             *property_name,
+                                                                             const GValue            *value,
+                                                                             XfceDisplaysHelper      *helper);
 
 
 
@@ -66,16 +98,33 @@ struct _XfceDisplaysHelper
     GObject  __parent__;
 
     /* xfconf channel */
-    XfconfChannel *channel;
+    XfconfChannel      *channel;
+    guint               handler;
 
 #ifdef HAS_RANDR_ONE_POINT_THREE
-    gint           has_1_3;
+    gint                has_1_3;
+    gint                primary;
 #endif
-};
 
-/* wrappers to avoid querying too often */
-typedef struct _XfceRRCrtc XfceRRCrtc;
-typedef struct _XfceRROutput XfceRROutput;
+    GdkDisplay         *display;
+    GdkWindow          *root_window;
+    Display            *xdisplay;
+    gint                event_base;
+
+    /* RandR cache */
+    XRRScreenResources *resources;
+    GPtrArray          *crtcs;
+
+    /* screen size */
+    gint                width;
+    gint                height;
+    gint                mm_width;
+    gint                mm_height;
+
+    /* used to normalize positions */
+    gint                min_x;
+    gint                min_y;
+};
 
 struct _XfceRRCrtc
 {
@@ -94,12 +143,6 @@ struct _XfceRRCrtc
     gint      changed;
 };
 
-struct _XfceRROutput
-{
-    RROutput       id;
-    XRROutputInfo *info;
-};
-
 
 
 G_DEFINE_TYPE (XfceDisplaysHelper, xfce_displays_helper, G_TYPE_OBJECT);
@@ -109,7 +152,10 @@ G_DEFINE_TYPE (XfceDisplaysHelper, xfce_displays_helper, G_TYPE_OBJECT);
 static void
 xfce_displays_helper_class_init (XfceDisplaysHelperClass *klass)
 {
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+    gobject_class->dispose = xfce_displays_helper_dispose;
+    gobject_class->finalize = xfce_displays_helper_finalize;
 }
 
 
@@ -118,15 +164,51 @@ static void
 xfce_displays_helper_init (XfceDisplaysHelper *helper)
 {
     gint major = 0, minor = 0;
-    gint event_base, error_base;
+    gint error_base, err;
+
+    helper->resources = NULL;
+    helper->crtcs = NULL;
+    helper->handler = 0;
+
+    /* get the default display */
+    helper->display = gdk_display_get_default ();
+    helper->xdisplay = gdk_x11_display_get_xdisplay (helper->display);
+    helper->root_window = gdk_get_default_root_window ();
 
     /* check if the randr extension is running */
-    if (XRRQueryExtension (GDK_DISPLAY (), &event_base, &error_base))
+    if (XRRQueryExtension (helper->xdisplay, &helper->event_base, &error_base))
     {
         /* query the version */
-        if (XRRQueryVersion (GDK_DISPLAY (), &major, &minor)
+        if (XRRQueryVersion (helper->xdisplay, &major, &minor)
             && (major > 1 || (major == 1 && minor >= 2)))
         {
+            gdk_error_trap_push ();
+            /* get the screen resource */
+            helper->resources = XRRGetScreenResources (helper->xdisplay,
+                                                       GDK_WINDOW_XID (helper->root_window));
+            gdk_flush ();
+            err = gdk_error_trap_pop ();
+            if (err)
+            {
+                g_critical ("XRRGetScreenResources failed (err: %d). "
+                            "Display settings won't be applied.", err);
+                return;
+            }
+
+            /* get all existing CRTCs */
+            helper->crtcs = xfce_displays_helper_list_crtcs (helper);
+
+            /* Set up RandR notifications */
+            XRRSelectInput (helper->xdisplay,
+                            GDK_WINDOW_XID (helper->root_window),
+                            RRScreenChangeNotifyMask);
+            gdk_x11_register_standard_event_type (helper->display,
+                                                  helper->event_base,
+                                                  RRNotify + 1);
+            gdk_window_add_filter (helper->root_window,
+                                   xfce_displays_helper_screen_on_event,
+                                   helper);
+
             /* open the channel */
             helper->channel = xfconf_channel_get ("displays");
 
@@ -134,8 +216,10 @@ xfce_displays_helper_init (XfceDisplaysHelper *helper)
             xfconf_channel_reset_property (helper->channel, "/Schemes/Apply", FALSE);
 
             /* monitor channel changes */
-            g_signal_connect (G_OBJECT (helper->channel), "property-changed",
-                              G_CALLBACK (xfce_displays_helper_channel_property_changed), helper);
+            helper->handler = g_signal_connect (G_OBJECT (helper->channel),
+                                                "property-changed",
+                                                G_CALLBACK (xfce_displays_helper_channel_property_changed),
+                                                helper);
 
 #ifdef HAS_RANDR_ONE_POINT_THREE
             helper->has_1_3 = (major > 1 || (major == 1 && minor >= 3));
@@ -153,209 +237,537 @@ xfce_displays_helper_init (XfceDisplaysHelper *helper)
     else
     {
         g_critical ("No RANDR extension found in display %s. Display settings won't be applied.",
-                    gdk_display_get_name (gdk_display_get_default ()));
+                    gdk_display_get_name (helper->display));
     }
 }
 
 
 
 static void
-xfce_displays_helper_process_screen_size (gint  mode_width,
-                                          gint  mode_height,
-                                          gint  crtc_pos_x,
-                                          gint  crtc_pos_y,
-                                          gint *width,
-                                          gint *height,
-                                          gint *mm_width,
-                                          gint *mm_height)
+xfce_displays_helper_dispose (GObject *object)
 {
-    g_assert (width && height && mm_width && mm_height);
+    XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (object);
 
-    *width = MAX (*width, crtc_pos_x + mode_width);
-    *height = MAX (*height, crtc_pos_y + mode_height);
+    if (helper->handler > 0)
+    {
+        g_signal_handler_disconnect (G_OBJECT (helper->channel),
+                                     helper->handler);
+        helper->handler = 0;
+    }
 
-    /* The 'physical size' of an X screen is meaningless if that screen
-     * can consist of many monitors. So just pick a size that make the
-     * dpi 96.
-     *
-     * Firefox and Evince apparently believe what X tells them.
-     */
-    *mm_width = (*width / 96.0) * 25.4 + 0.5;
-    *mm_height = (*height / 96.0) * 25.4 + 0.5;
+    gdk_window_remove_filter (helper->root_window,
+                              xfce_displays_helper_screen_on_event,
+                              helper);
+
+    (*G_OBJECT_CLASS (xfce_displays_helper_parent_class)->dispose) (object);
 }
 
 
 
-static XfceRRCrtc *
-xfce_displays_helper_list_crtcs (Display            *xdisplay,
-                                 XRRScreenResources *resources)
+static void
+xfce_displays_helper_finalize (GObject *object)
 {
-    XfceRRCrtc  *crtcs;
-    XRRCrtcInfo *crtc_info;
-    gint         n;
+    XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (object);
 
-    g_assert (xdisplay && resources);
+    /* Free the CRTC cache */
+    if (helper->crtcs)
+    {
+        g_ptr_array_free (helper->crtcs, TRUE);
+        helper->crtcs = NULL;
+    }
 
-    /* get all existing CRTCs */
-    crtcs = g_new0 (XfceRRCrtc, resources->ncrtc);
-    for (n = 0; n < resources->ncrtc; ++n)
-    {
-        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected CRTC %lu.", resources->crtcs[n]);
-
-        crtcs[n].id = resources->crtcs[n];
-        crtc_info = XRRGetCrtcInfo (xdisplay, resources, resources->crtcs[n]);
-        crtcs[n].mode = crtc_info->mode;
-        crtcs[n].rotation = crtc_info->rotation;
-        crtcs[n].rotations = crtc_info->rotations;
-        crtcs[n].width = crtc_info->width;
-        crtcs[n].height = crtc_info->height;
-        crtcs[n].x = crtc_info->x;
-        crtcs[n].y = crtc_info->y;
-
-        crtcs[n].noutput = crtc_info->noutput;
-        crtcs[n].outputs = NULL;
-        if (crtc_info->noutput > 0)
-            crtcs[n].outputs = g_memdup (crtc_info->outputs,
-                                         crtc_info->noutput * sizeof (RROutput));
+    /* Free the screen resources */
+    if (helper->resources)
+    {
+        gdk_error_trap_push ();
+        XRRFreeScreenResources (helper->resources);
+        gdk_flush ();
+        gdk_error_trap_pop ();
+        helper->resources = NULL;
+    }
 
-        crtcs[n].npossible = crtc_info->npossible;
-        crtcs[n].possible = NULL;
-        if (crtc_info->npossible > 0)
-            crtcs[n].possible = g_memdup (crtc_info->possible,
-                                          crtc_info->npossible * sizeof (RROutput));
+    (*G_OBJECT_CLASS (xfce_displays_helper_parent_class)->finalize) (object);
+}
 
-        crtcs[n].changed = FALSE;
-        XRRFreeCrtcInfo (crtc_info);
-    }
 
-    return crtcs;
+
+static void
+xfce_displays_helper_reload (XfceDisplaysHelper *helper)
+{
+    gint err;
+
+    xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Refreshing RandR cache.");
+
+    /* Free the CRTC cache */
+    g_ptr_array_free (helper->crtcs, TRUE);
+
+    gdk_error_trap_push ();
+
+    /* Free the screen resources */
+    XRRFreeScreenResources (helper->resources);
+
+    /* get the screen resource */
+#ifdef HAS_RANDR_ONE_POINT_THREE
+    /* xfce_displays_helper_reload () is usually called after a xrandr notification,
+       which means that X is aware of the new hardware already. So, if possible,
+       do not reprobe the hardware again. */
+    if (helper->has_1_3)
+        helper->resources = XRRGetScreenResourcesCurrent (helper->xdisplay,
+                                                          GDK_WINDOW_XID (helper->root_window));
+    else
+#endif
+    helper->resources = XRRGetScreenResources (helper->xdisplay,
+                                               GDK_WINDOW_XID (helper->root_window));
+
+    gdk_flush ();
+    err = gdk_error_trap_pop ();
+    if (err)
+        g_critical ("Failed to reload the RandR cache (err: %d).", err);
+
+    /* get all existing CRTCs */
+    helper->crtcs = xfce_displays_helper_list_crtcs (helper);
 }
 
 
 
-static XfceRRCrtc *
-xfce_displays_helper_find_crtc_by_id (XRRScreenResources *resources,
-                                      XfceRRCrtc         *crtcs,
-                                      RRCrtc              id)
+static GdkFilterReturn
+xfce_displays_helper_screen_on_event (GdkXEvent *xevent,
+                                      GdkEvent  *event,
+                                      gpointer   data)
 {
-    gint n;
+    XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (data);
+    XEvent             *e = xevent;
+    gint                event_num;
+
+    if (!e)
+        return GDK_FILTER_CONTINUE;
 
-    g_assert (resources && crtcs);
+    event_num = e->type - helper->event_base;
 
-    for (n = 0; n < resources->ncrtc; ++n)
+    if (event_num == RRScreenChangeNotify)
     {
-        if (crtcs[n].id == id)
-            return &crtcs[n];
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "RRScreenChangeNotify event received.");
+
+        xfce_displays_helper_reload (helper);
+
+        /*TODO: check that there is still one output enabled */
+        /*TODO: e.g. reenable LVDS1 when VGA1 is diconnected. */
+        /*TODO: also, disable LVDS1 when the lid is closed (needs upower though :/) */
     }
 
-    return NULL;
+    /* Pass the event on to GTK+ */
+    return GDK_FILTER_CONTINUE;
 }
 
 
 
 static void
-xfce_displays_helper_cleanup_crtc (XfceRRCrtc *crtc)
+xfce_displays_helper_set_screen_size (XfceDisplaysHelper *helper)
 {
-    if (crtc == NULL)
+    gint min_width, min_height, max_width, max_height;
+
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources);
+
+    /* get the screen size extremums */
+    if (!XRRGetScreenSizeRange (helper->xdisplay, GDK_WINDOW_XID (helper->root_window),
+                                &min_width, &min_height, &max_width, &max_height))
+    {
+        g_warning ("Unable to get the range of screen sizes. "
+                   "Display settings may fail to apply.");
         return;
+    }
 
-    if (crtc->outputs != NULL)
-        g_free (crtc->outputs);
-    if (crtc->possible != NULL)
-        g_free (crtc->possible);
+    /* set the screen size only if it's really needed and valid */
+    if (helper->width >= min_width && helper->width <= max_width
+        && helper->height >= min_height && helper->height <= max_height
+        && (helper->width != gdk_screen_width ()
+            || helper->height != gdk_screen_height ()
+            || helper->mm_width != gdk_screen_width_mm ()
+            || helper->mm_height != gdk_screen_height_mm ()))
+    {
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying desktop dimensions: %dx%d (px), %dx%d (mm).",
+                        helper->width, helper->height, helper->mm_width, helper->mm_height);
+        XRRSetScreenSize (helper->xdisplay, GDK_WINDOW_XID (helper->root_window),
+                          helper->width, helper->height, helper->mm_width, helper->mm_height);
+    }
 }
 
 
 
-static void
-xfce_displays_helper_free_output (XfceRROutput *output)
+static gboolean
+xfce_displays_helper_load_from_xfconf (XfceDisplaysHelper *helper,
+                                       const gchar        *scheme,
+                                       GHashTable         *saved_outputs,
+                                       RROutput            output)
 {
-    if (output == NULL)
-        return;
+    XfceRRCrtc    *crtc = NULL;
+    XRROutputInfo *info;
+    GValue        *value;
+    const gchar   *str_value;
+    gchar          property[512];
+    gdouble        output_rate, rate;
+    RRMode         valid_mode;
+    Rotation       rot;
+    gint           x, y, n, m, int_value, err;
+    gboolean       active = FALSE;
 
-    XRRFreeOutputInfo (output->info);
-    g_free (output);
+    gdk_error_trap_push ();
+    info = XRRGetOutputInfo (helper->xdisplay, helper->resources, output);
+    gdk_flush ();
+    err = gdk_error_trap_pop ();
+    if (err || !info)
+    {
+        g_warning ("Failed to load info for output %lu (err: %d). Skipping.",
+                   output, err);
+        return FALSE;
+    }
+
+    xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected output %lu %s.", output,
+                    info->name);
+
+    /* ignore disconnected outputs */
+    if (info->connection != RR_Connected)
+        goto next_output;
+
+    /* Get the associated CRTC */
+    if (info->crtc != None)
+        crtc = xfce_displays_helper_find_crtc_by_id (helper, info->crtc);
+
+    /* track active outputs */
+    if (crtc && crtc->mode != None)
+    {
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s is active.", info->name);
+        active = TRUE;
+    }
+
+    /* does this output exist in xfconf? */
+    g_snprintf (property, sizeof (property), "/%s/%s", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+
+    if (value == NULL || !G_VALUE_HOLDS_STRING (value))
+        goto next_output;
+
+#ifdef HAS_RANDR_ONE_POINT_THREE
+    if (helper->has_1_3)
+    {
+        /* is it the primary output? */
+        g_snprintf (property, sizeof (property), "/%s/%s/Primary", scheme, info->name);
+        value = g_hash_table_lookup (saved_outputs, property);
+        if (G_VALUE_HOLDS_BOOLEAN (value) && g_value_get_boolean (value))
+            helper->primary = output;
+    }
+#endif
+
+    /* status */
+    g_snprintf (property, sizeof (property), "/%s/%s/Active", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+
+    if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value))
+        goto next_output;
+
+    /* No existing CRTC, try to find a free one */
+    if (info->crtc == None)
+        crtc = xfce_displays_helper_find_usable_crtc (helper, output);
+
+    if (!crtc)
+    {
+        g_warning ("No available CRTC for %s, aborting.", info->name);
+        goto next_output;
+    }
+    xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu assigned to %s.", crtc->id, info->name);
+
+    /* disable inactive outputs */
+    if (!g_value_get_boolean (value))
+    {
+        if (crtc->mode != None)
+        {
+            active = FALSE;
+            xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s will be disabled by configuration.", info->name);
+
+            crtc->mode = None;
+            crtc->noutput = 0;
+            crtc->changed = TRUE;
+        }
+
+        goto next_output;
+    }
+
+    /* rotation */
+    g_snprintf (property, sizeof (property), "/%s/%s/Rotation", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (G_VALUE_HOLDS_INT (value))
+        int_value = g_value_get_int (value);
+    else
+        int_value = 0;
+
+    /* convert to a Rotation */
+    switch (int_value)
+    {
+        case 90:  rot = RR_Rotate_90;  break;
+        case 180: rot = RR_Rotate_180; break;
+        case 270: rot = RR_Rotate_270; break;
+        default:  rot = RR_Rotate_0;   break;
+    }
+
+    /* reflection */
+    g_snprintf (property, sizeof (property), "/%s/%s/Reflection", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (G_VALUE_HOLDS_STRING (value))
+        str_value = g_value_get_string (value);
+    else
+        str_value = "0";
+
+    /* convert to a Rotation */
+    if (g_strcmp0 (str_value, "X") == 0)
+        rot |= RR_Reflect_X;
+    else if (g_strcmp0 (str_value, "Y") == 0)
+        rot |= RR_Reflect_Y;
+    else if (g_strcmp0 (str_value, "XY") == 0)
+        rot |= (RR_Reflect_X|RR_Reflect_Y);
+
+    /* check rotation support */
+    if ((crtc->rotations & rot) == 0)
+    {
+        g_warning ("Unsupported rotation for %s. Fallback to RR_Rotate_0.", info->name);
+        rot = RR_Rotate_0;
+    }
+
+    /* update CRTC rotation */
+    if (crtc->rotation != rot)
+    {
+        crtc->rotation = rot;
+        crtc->changed = TRUE;
+    }
+
+    /* resolution */
+    g_snprintf (property, sizeof (property), "/%s/%s/Resolution", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (value == NULL || !G_VALUE_HOLDS_STRING (value))
+        str_value = "";
+    else
+        str_value = g_value_get_string (value);
+
+    /* refresh rate */
+    g_snprintf (property, sizeof (property), "/%s/%s/RefreshRate", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (G_VALUE_HOLDS_DOUBLE (value))
+        output_rate = g_value_get_double (value);
+    else
+        output_rate = 0.0;
+
+    /* check mode validity */
+    valid_mode = None;
+    for (n = 0; n < info->nmode; ++n)
+    {
+        /* walk all modes */
+        for (m = 0; m < helper->resources->nmode; ++m)
+        {
+            /* does the mode info match the mode we seek? */
+            if (helper->resources->modes[m].id != info->modes[n])
+                continue;
+
+            /* calculate the refresh rate */
+            rate = (gdouble) helper->resources->modes[m].dotClock /
+                    ((gdouble) helper->resources->modes[m].hTotal * (gdouble) helper->resources->modes[m].vTotal);
+
+            /* find the mode corresponding to the saved values */
+            if (rint (rate) == rint (output_rate)
+                && (g_strcmp0 (helper->resources->modes[m].name, str_value) == 0))
+            {
+                valid_mode = helper->resources->modes[m].id;
+                break;
+            }
+        }
+        /* found it */
+        if (valid_mode != None)
+            break;
+    }
+
+    if (valid_mode == None)
+    {
+        /* unsupported mode, abort for this output */
+        g_warning ("Unknown mode '%s @ %.1f' for output %s, aborting.",
+                   str_value, output_rate, info->name);
+        goto next_output;
+    }
+    else if (crtc->mode != valid_mode)
+    {
+        if (crtc->mode == None)
+            active = TRUE;
+
+        /* update CRTC mode */
+        crtc->mode = valid_mode;
+        crtc->changed = TRUE;
+    }
+
+    /* recompute dimensions according to the selected rotation */
+    if ((crtc->rotation & (RR_Rotate_90|RR_Rotate_270)) != 0)
+    {
+        crtc->width = helper->resources->modes[m].height;
+        crtc->height = helper->resources->modes[m].width;
+    }
+    else
+    {
+        crtc->width = helper->resources->modes[m].width;
+        crtc->height = helper->resources->modes[m].height;
+    }
+
+    /* position, x */
+    g_snprintf (property, sizeof (property), "/%s/%s/Position/X", scheme, info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (G_VALUE_HOLDS_INT (value))
+        x = g_value_get_int (value);
+    else
+        x = 0;
+
+    /* position, y */
+    g_snprintf (property, sizeof (property), "/%s/%s/Position/Y", scheme,
+                info->name);
+    value = g_hash_table_lookup (saved_outputs, property);
+    if (G_VALUE_HOLDS_INT (value))
+        y = g_value_get_int (value);
+    else
+        y = 0;
+
+    /* update CRTC position */
+    if (crtc->x != x || crtc->y != y)
+    {
+        crtc->x = x;
+        crtc->y = y;
+        crtc->changed = TRUE;
+    }
+
+    /* used to normalize positions later */
+    helper->min_x = MIN (helper->min_x, crtc->x);
+    helper->min_y = MIN (helper->min_y, crtc->y);
+
+    xfce_displays_helper_set_outputs (crtc, output);
+
+next_output:
+    XRRFreeOutputInfo (info);
+    return active;
 }
 
 
 
 static GPtrArray *
-xfce_displays_helper_list_outputs (Display            *xdisplay,
-                                   XRRScreenResources *resources,
-                                   XfceRRCrtc         *crtcs,
-                                   gint               *nactive)
+xfce_displays_helper_list_crtcs (XfceDisplaysHelper *helper)
 {
-    GPtrArray     *outputs;
-    XRROutputInfo *info;
-    XfceRROutput  *output;
-    XfceRRCrtc    *crtc;
-    gint           n;
-
-    g_assert (xdisplay && resources && nactive);
+    GPtrArray   *crtcs;
+    XRRCrtcInfo *crtc_info;
+    XfceRRCrtc  *crtc;
+    gint         n, err;
 
-    outputs = g_ptr_array_new ();
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources);
 
-    /* get all connected outputs */
-    *nactive = 0;
-    for (n = 0; n < resources->noutput; ++n)
+    /* get all existing CRTCs */
+    crtcs = g_ptr_array_new_with_free_func ((GDestroyNotify) xfce_displays_helper_free_crtc);
+    for (n = 0; n < helper->resources->ncrtc; ++n)
     {
-        info = XRRGetOutputInfo (xdisplay, resources, resources->outputs[n]);
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected CRTC %lu.", helper->resources->crtcs[n]);
 
-        if (info->connection != RR_Connected)
+        gdk_error_trap_push ();
+        crtc_info = XRRGetCrtcInfo (helper->xdisplay, helper->resources, helper->resources->crtcs[n]);
+        gdk_flush ();
+        err = gdk_error_trap_pop ();
+        if (err || !crtc_info)
         {
-            XRRFreeOutputInfo (info);
+            g_warning ("Failed to load info for CRTC %lu (err: %d). Skipping.",
+                       helper->resources->crtcs[n], err);
             continue;
         }
 
-        output = g_new0 (XfceRROutput, 1);
-        output->id = resources->outputs[n];
-        output->info = info;
+        crtc = g_new0 (XfceRRCrtc, 1);
+        crtc->id = helper->resources->crtcs[n];
+        crtc->mode = crtc_info->mode;
+        crtc->rotation = crtc_info->rotation;
+        crtc->rotations = crtc_info->rotations;
+        crtc->width = crtc_info->width;
+        crtc->height = crtc_info->height;
+        crtc->x = crtc_info->x;
+        crtc->y = crtc_info->y;
+
+        crtc->noutput = crtc_info->noutput;
+        crtc->outputs = NULL;
+        if (crtc_info->noutput > 0)
+            crtc->outputs = g_memdup (crtc_info->outputs,
+                                      crtc_info->noutput * sizeof (RROutput));
 
-        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected output %lu %s.", output->id,
-                        output->info->name);
+        crtc->npossible = crtc_info->npossible;
+        crtc->possible = NULL;
+        if (crtc_info->npossible > 0)
+            crtc->possible = g_memdup (crtc_info->possible,
+                                       crtc_info->npossible * sizeof (RROutput));
 
-        /* track active outputs */
-        crtc = xfce_displays_helper_find_crtc_by_id (resources, crtcs,
-                                                     output->info->crtc);
-        if (crtc && crtc->mode != None)
-        {
-            xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s is active.", output->info->name);
-            ++(*nactive);
-        }
+        crtc->changed = FALSE;
+        XRRFreeCrtcInfo (crtc_info);
 
         /* cache it */
-        g_ptr_array_add (outputs, output);
+        g_ptr_array_add (crtcs, crtc);
     }
 
-    return outputs;
+    return crtcs;
 }
 
 
 
 static XfceRRCrtc *
-xfce_displays_helper_find_usable_crtc (XRRScreenResources *resources,
-                                       XfceRRCrtc         *crtcs,
-                                       XfceRROutput       *output)
+xfce_displays_helper_find_crtc_by_id (XfceDisplaysHelper *helper,
+                                      RRCrtc              id)
+{
+    XfceRRCrtc *crtc;
+    guint       n;
+
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->crtcs);
+
+    for (n = 0; n < helper->crtcs->len; ++n)
+    {
+        crtc = g_ptr_array_index (helper->crtcs, n);
+        if (crtc->id == id)
+            return crtc;
+    }
+
+    return NULL;
+}
+
+
+
+static void
+xfce_displays_helper_free_crtc (XfceRRCrtc *crtc)
 {
-    gint m, n;
+    if (crtc == NULL)
+        return;
+
+    if (crtc->outputs != NULL)
+        g_free (crtc->outputs);
+    if (crtc->possible != NULL)
+        g_free (crtc->possible);
+    g_free (crtc);
+}
 
-    g_assert (resources && crtcs && output);
 
-    /* if there is one already assigned, return it */
-    if (output->info->crtc != None)
-        return xfce_displays_helper_find_crtc_by_id (resources, crtcs,
-                                                     output->info->crtc);
+
+static XfceRRCrtc *
+xfce_displays_helper_find_usable_crtc (XfceDisplaysHelper *helper,
+                                       RROutput            output)
+{
+    XfceRRCrtc *crtc;
+    guint       n;
+    gint        m;
+
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->crtcs);
 
     /* try to find one that is not already used by another output */
-    for (n = 0; n < resources->ncrtc; ++n)
+    for (n = 0; n < helper->crtcs->len; ++n)
     {
-        if (crtcs[n].noutput > 0 || crtcs[n].changed)
+        crtc = g_ptr_array_index (helper->crtcs, n);
+        if (crtc->noutput > 0 || crtc->changed)
             continue;
 
-        for (m = 0; m < crtcs[n].npossible; ++m)
+        for (m = 0; m < crtc->npossible; ++m)
         {
-            if (crtcs[n].possible[m] == output->id)
-                return &crtcs[n];
+            if (crtc->possible[m] == output)
+                return crtc;
         }
     }
 
@@ -365,43 +777,110 @@ xfce_displays_helper_find_usable_crtc (XRRScreenResources *resources,
 
 
 
+static void
+xfce_displays_helper_normalize_crtc (XfceRRCrtc         *crtc,
+                                     XfceDisplaysHelper *helper)
+{
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && crtc);
+
+    /* ignore disabled outputs for size computations */
+    if (crtc->mode == None)
+        return;
+
+    /* normalize positions to ensure the upper left corner is at (0,0) */
+    if (helper->min_x || helper->min_y)
+    {
+        crtc->x -= helper->min_x;
+        crtc->y -= helper->min_y;
+        crtc->changed = TRUE;
+    }
+
+    xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Normalized CRTC %lu: size=%dx%d, pos=%dx%d.",
+                    crtc->id, crtc->width, crtc->height, crtc->x, crtc->y);
+
+    /* calculate the total screen size */
+    helper->width = MAX (helper->width, crtc->x + crtc->width);
+    helper->height = MAX (helper->height, crtc->y + crtc->height);
+
+    /* The 'physical size' of an X screen is meaningless if that screen
+     * can consist of many monitors. So just pick a size that make the
+     * dpi 96.
+     *
+     * Firefox and Evince apparently believe what X tells them.
+     */
+    helper->mm_width = (helper->width / 96.0) * 25.4 + 0.5;
+    helper->mm_height = (helper->height / 96.0) * 25.4 + 0.5;
+}
+
+
+
 static Status
-xfce_displays_helper_disable_crtc (Display            *xdisplay,
-                                   XRRScreenResources *resources,
+xfce_displays_helper_disable_crtc (XfceDisplaysHelper *helper,
                                    RRCrtc              crtc)
 {
-    g_assert (xdisplay && resources);
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources);
 
     xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Disabling CRTC %lu.", crtc);
 
-    return XRRSetCrtcConfig (xdisplay, resources, crtc, CurrentTime,
-                             0, 0, None, RR_Rotate_0, NULL, 0);
+    return XRRSetCrtcConfig (helper->xdisplay, helper->resources, crtc,
+                             CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0);
 }
 
 
 
-static Status
-xfce_displays_helper_apply_crtc (Display            *xdisplay,
-                                 XRRScreenResources *resources,
-                                 XfceRRCrtc         *crtc)
+static void
+xfce_displays_helper_workaround_crtc_size (XfceRRCrtc         *crtc,
+                                           XfceDisplaysHelper *helper)
+{
+    XRRCrtcInfo *crtc_info;
+
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources && crtc);
+
+    /* The CRTC needs to be disabled if its previous mode won't fit in the new screen.
+       It will be reenabled with its new mode (known to fit) after the screen size is
+       changed, unless the user disabled it (no need to reenable it then). */
+    crtc_info = XRRGetCrtcInfo (helper->xdisplay, helper->resources, crtc->id);
+    if ((crtc_info->x + crtc_info->width > (guint) helper->width) ||
+        (crtc_info->y + crtc_info->height > (guint) helper->height))
+    {
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu must be temporarily disabled.", crtc->id);
+        if (xfce_displays_helper_disable_crtc (helper, crtc->id) == RRSetConfigSuccess)
+            crtc->changed = (crtc->mode != None);
+        else
+            g_warning ("Failed to temporarily disable CRTC %lu.", crtc->id);
+    }
+    XRRFreeCrtcInfo (crtc_info);
+}
+
+
+
+static void
+xfce_displays_helper_apply_crtc (XfceRRCrtc         *crtc,
+                                 XfceDisplaysHelper *helper)
 {
     Status ret;
 
-    g_assert (xdisplay && resources && crtc);
+    g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources && crtc);
 
     xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Configuring CRTC %lu.", crtc->id);
 
-    if (crtc->mode == None)
-        ret = xfce_displays_helper_disable_crtc (xdisplay, resources, crtc->id);
-    else
-        ret = XRRSetCrtcConfig (xdisplay, resources, crtc->id, CurrentTime,
-                                crtc->x, crtc->y, crtc->mode, crtc->rotation,
-                                crtc->outputs, crtc->noutput);
+    /* check if we really need to do something */
+    if (crtc->changed)
+    {
+        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying changes to CRTC %lu.", crtc->id);
 
-    if (ret == RRSetConfigSuccess)
-        crtc->changed = FALSE;
+        if (crtc->mode == None)
+            ret = xfce_displays_helper_disable_crtc (helper, crtc->id);
+        else
+            ret = XRRSetCrtcConfig (helper->xdisplay, helper->resources, crtc->id,
+                                    CurrentTime, crtc->x, crtc->y, crtc->mode,
+                                    crtc->rotation, crtc->outputs, crtc->noutput);
 
-    return ret;
+        if (ret == RRSetConfigSuccess)
+            crtc->changed = FALSE;
+        else
+            g_warning ("Failed to configure CRTC %lu.", crtc->id);
+    }
 }
 
 
@@ -446,58 +925,16 @@ static void
 xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper,
                                     const gchar        *scheme)
 {
-    GdkDisplay         *display;
-    Display            *xdisplay;
-    GdkWindow          *root_window;
-    XRRScreenResources *resources;
-    XRRCrtcInfo        *crtc_info;
-    XfceRRCrtc         *crtcs, *crtc;
-    gchar               property[512];
-    gint                min_width, min_height, max_width, max_height;
-    gint                mm_width, mm_height, width, height;
-    gint                x, y, min_x, min_y;
-    gint                l, m, int_value, nactive;
-    guint               n;
-    GValue             *value;
-    const gchar        *str_value;
-    gdouble             output_rate, rate;
-    RRMode              valid_mode;
-    Rotation            rot;
-    GPtrArray          *connected_outputs;
-    GHashTable         *saved_outputs;
-    XfceRROutput       *output;
-#ifdef HAS_RANDR_ONE_POINT_THREE
-    RROutput            primary = None;
-#endif
-
-    gdk_error_trap_push ();
+    gchar       property[512];
+    gint        n, nactive;
+    GHashTable *saved_outputs;
 
     saved_outputs = NULL;
-
-    /* get the default display */
-    display = gdk_display_get_default ();
-    xdisplay = gdk_x11_display_get_xdisplay (display);
-    root_window = gdk_get_default_root_window ();
-
-    /* get the screen resource */
-    resources = XRRGetScreenResources (xdisplay, GDK_WINDOW_XID (root_window));
-
-    /* get the range of screen sizes */
-    mm_width = mm_height = width = height = 0;
-    min_x = min_y = 32768;
-    if (!XRRGetScreenSizeRange (xdisplay, GDK_WINDOW_XID (root_window),
-                                &min_width, &min_height, &max_width, &max_height))
-    {
-        g_critical ("Unable to get the range of screen sizes, aborting.");
-        goto err_abort;
-    }
-
-    /* get all existing CRTCs */
-    crtcs = xfce_displays_helper_list_crtcs (xdisplay, resources);
-
-    /* then all connected outputs */
-    connected_outputs = xfce_displays_helper_list_outputs (xdisplay, resources,
-                                                           crtcs, &nactive);
+    helper->mm_width = helper->mm_height = helper->width = helper->height = 0;
+    helper->min_x = helper->min_y = 32768;
+#ifdef HAS_RANDR_ONE_POINT_THREE
+    helper->primary = None;
+#endif
 
     /* finally the list of saved outputs from xfconf */
     g_snprintf (property, sizeof (property), "/%s", scheme);
@@ -508,221 +945,12 @@ xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper,
         goto err_cleanup;
 
     /* first loop, loads all the outputs, and gets the number of active ones */
-    for (n = 0; n < connected_outputs->len; ++n)
+    nactive = 0;
+    for (n = 0; n < helper->resources->noutput; ++n)
     {
-        output = g_ptr_array_index (connected_outputs, n);
-
-        /* does this output exist in xfconf? */
-        g_snprintf (property, sizeof (property), "/%s/%s", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-
-        if (value == NULL || !G_VALUE_HOLDS_STRING (value))
-            continue;
-
-#ifdef HAS_RANDR_ONE_POINT_THREE
-        if (helper->has_1_3)
-        {
-            /* is it the primary output? */
-            g_snprintf (property, sizeof (property), "/%s/%s/Primary", scheme,
-                        output->info->name);
-            value = g_hash_table_lookup (saved_outputs, property);
-            if (G_VALUE_HOLDS_BOOLEAN (value) && g_value_get_boolean (value))
-                primary = output->id;
-        }
-#endif
-
-        /* status */
-        g_snprintf (property, sizeof (property), "/%s/%s/Active", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-
-        if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value))
-            continue;
-
-        /* Pick a CRTC for this output */
-        crtc = xfce_displays_helper_find_usable_crtc (resources, crtcs, output);
-        if (!crtc)
-        {
-            g_warning ("No available CRTC for %s, aborting.", output->info->name);
-            continue;
-        }
-        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu assigned to %s.", crtc->id,
-                        output->info->name);
-
-        /* disable inactive outputs */
-        if (!g_value_get_boolean (value))
-        {
-            if (crtc->mode != None)
-            {
-                --nactive;
-                xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s will be disabled by configuration.",
-                                output->info->name);
-
-                crtc->mode = None;
-                crtc->noutput = 0;
-                crtc->changed = TRUE;
-            }
-
-            continue;
-        }
-
-        /* rotation */
-        g_snprintf (property, sizeof (property), "/%s/%s/Rotation", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (G_VALUE_HOLDS_INT (value))
-            int_value = g_value_get_int (value);
-        else
-            int_value = 0;
-
-        /* convert to a Rotation */
-        switch (int_value)
-        {
-            case 90:  rot = RR_Rotate_90;  break;
-            case 180: rot = RR_Rotate_180; break;
-            case 270: rot = RR_Rotate_270; break;
-            default:  rot = RR_Rotate_0;   break;
-        }
-
-        /* reflection */
-        g_snprintf (property, sizeof (property), "/%s/%s/Reflection", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (G_VALUE_HOLDS_STRING (value))
-            str_value = g_value_get_string (value);
-        else
-            str_value = "0";
-
-        /* convert to a Rotation */
-        if (g_strcmp0 (str_value, "X") == 0)
-            rot |= RR_Reflect_X;
-        else if (g_strcmp0 (str_value, "Y") == 0)
-            rot |= RR_Reflect_Y;
-        else if (g_strcmp0 (str_value, "XY") == 0)
-            rot |= (RR_Reflect_X|RR_Reflect_Y);
-
-        /* check rotation support */
-        if ((crtc->rotations & rot) == 0)
-        {
-            g_warning ("Unsupported rotation for %s. Fallback to RR_Rotate_0.",
-                       output->info->name);
-            rot = RR_Rotate_0;
-        }
-
-        /* update CRTC rotation */
-        if (crtc->rotation != rot)
-        {
-            crtc->rotation = rot;
-            crtc->changed = TRUE;
-        }
-
-        /* resolution */
-        g_snprintf (property, sizeof (property), "/%s/%s/Resolution",
-                    scheme, output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (value == NULL || !G_VALUE_HOLDS_STRING (value))
-            str_value = "";
-        else
-            str_value = g_value_get_string (value);
-
-        /* refresh rate */
-        g_snprintf (property, sizeof (property), "/%s/%s/RefreshRate", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (G_VALUE_HOLDS_DOUBLE (value))
-            output_rate = g_value_get_double (value);
-        else
-            output_rate = 0.0;
-
-        /* check mode validity */
-        valid_mode = None;
-        for (m = 0; m < output->info->nmode; ++m)
-        {
-            /* walk all modes */
-            for (l = 0; l < resources->nmode; ++l)
-            {
-                /* does the mode info match the mode we seek? */
-                if (resources->modes[l].id != output->info->modes[m])
-                    continue;
-
-                /* calculate the refresh rate */
-                rate = (gdouble) resources->modes[l].dotClock /
-                        ((gdouble) resources->modes[l].hTotal * (gdouble) resources->modes[l].vTotal);
-
-                /* find the mode corresponding to the saved values */
-                if (rint (rate) == rint (output_rate)
-                    && (g_strcmp0 (resources->modes[l].name, str_value) == 0))
-                {
-                    valid_mode = resources->modes[l].id;
-                    break;
-                }
-            }
-            /* found it */
-            if (valid_mode != None)
-                break;
-        }
-
-        if (valid_mode == None)
-        {
-            /* unsupported mode, abort for this output */
-            g_warning ("Unknown mode '%s @ %.1f' for output %s, aborting.",
-                       str_value, output_rate, output->info->name);
-            continue;
-        }
-        else if (crtc->mode != valid_mode)
-        {
-            if (crtc->mode == None)
-                ++nactive;
-
-            /* update CRTC mode */
-            crtc->mode = valid_mode;
-            crtc->changed = TRUE;
-        }
-
-        /* recompute dimensions according to the selected rotation */
-        if ((crtc->rotation & (RR_Rotate_90|RR_Rotate_270)) != 0)
-        {
-            crtc->width = resources->modes[l].height;
-            crtc->height = resources->modes[l].width;
-        }
-        else
-        {
-            crtc->width = resources->modes[l].width;
-            crtc->height = resources->modes[l].height;
-        }
-
-        /* position, x */
-        g_snprintf (property, sizeof (property), "/%s/%s/Position/X", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (G_VALUE_HOLDS_INT (value))
-            x = g_value_get_int (value);
-        else
-            x = 0;
-
-        /* position, y */
-        g_snprintf (property, sizeof (property), "/%s/%s/Position/Y", scheme,
-                    output->info->name);
-        value = g_hash_table_lookup (saved_outputs, property);
-        if (G_VALUE_HOLDS_INT (value))
-            y = g_value_get_int (value);
-        else
-            y = 0;
-
-        /* update CRTC position */
-        if (crtc->x != x || crtc->y != y)
-        {
-            crtc->x = x;
-            crtc->y = y;
-            crtc->changed = TRUE;
-        }
-
-        /* used to normalize positions later */
-        min_x = MIN (min_x, crtc->x);
-        min_y = MIN (min_y, crtc->y);
-
-        xfce_displays_helper_set_outputs (crtc, output->id);
+        if (xfce_displays_helper_load_from_xfconf (helper, scheme, saved_outputs,
+                                                   helper->resources->outputs[n]))
+            ++nactive;
     }
 
     xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Total %d active output(s).", nactive);
@@ -734,109 +962,41 @@ xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper,
         goto err_cleanup;
     }
 
-    /* grab server to prevent clients from thinking no output is enabled */
-    gdk_x11_display_grab (display);
-
     /* second loop, normalization and screen size calculation */
-    for (m = 0; m < resources->ncrtc; ++m)
-    {
-        /* ignore disabled outputs for size computations */
-        if (crtcs[m].mode == None)
-            continue;
+    g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_normalize_crtc, helper);
 
-        /* normalize positions to ensure the upper left corner is at (0,0) */
-        if (min_x || min_y)
-        {
-            crtcs[m].x -= min_x;
-            crtcs[m].y -= min_y;
-            crtcs[m].changed = TRUE;
-        }
-
-        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Normalized CRTC %lu: size=%dx%d, pos=%dx%d.",
-                        crtcs[m].id, crtcs[m].width, crtcs[m].height, crtcs[m].x, crtcs[m].y);
+    gdk_error_trap_push ();
 
-        /* calculate the total screen size */
-        xfce_displays_helper_process_screen_size (crtcs[m].width, crtcs[m].height,
-                                                  crtcs[m].x, crtcs[m].y, &width,
-                                                  &height, &mm_width, &mm_height);
-    }
+    /* grab server to prevent clients from thinking no output is enabled */
+    gdk_x11_display_grab (helper->display);
 
     /* disable CRTCs that won't fit in the new screen */
-    for (m = 0; m < resources->ncrtc; ++m)
-    {
-        /* The CRTC needs to be disabled if its previous mode won't fit in the new screen.
-           It will be reenabled with its new mode (known to fit) after the screen size is
-           changed, unless the user disabled it (no need to reenable it then). */
-        crtc_info = XRRGetCrtcInfo (xdisplay, resources, crtcs[m].id);
-        if ((crtc_info->x + crtc_info->width > (guint) width) ||
-            (crtc_info->y + crtc_info->height > (guint) height))
-        {
-            xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu must be temporarily disabled.", crtcs[m].id);
-            if (xfce_displays_helper_disable_crtc (xdisplay, resources, crtcs[m].id) == RRSetConfigSuccess)
-                crtcs[m].changed = (crtcs[m].mode != None);
-            else
-                g_warning ("Failed to temporarily disable CRTC %lu.", crtc->id);
-        }
-        XRRFreeCrtcInfo (crtc_info);
-    }
+    g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_workaround_crtc_size, helper);
 
     /* set the screen size only if it's really needed and valid */
-    if (width >= min_width && width <= max_width
-        && height >= min_height && height <= max_height
-        && (width != gdk_screen_width ()
-            || height != gdk_screen_height ()
-            || mm_width != gdk_screen_width_mm ()
-            || mm_height != gdk_screen_height_mm ()))
-    {
-        xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying desktop dimensions: %dx%d (px), %dx%d (mm).",
-                        width, height, mm_width, mm_height);
-        XRRSetScreenSize (xdisplay, GDK_WINDOW_XID (root_window),
-                          width, height, mm_width, mm_height);
-    }
+    xfce_displays_helper_set_screen_size (helper);
 
     /* final loop, apply crtc changes */
-    for (m = 0; m < resources->ncrtc; ++m)
-    {
-        /* check if we really need to do something */
-        if (crtcs[m].changed)
-        {
-            xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying changes to CRTC %lu.", crtcs[m].id);
-
-            if (xfce_displays_helper_apply_crtc (xdisplay, resources, &crtcs[m]) != RRSetConfigSuccess)
-                g_warning ("Failed to configure CRTC %lu.", crtcs[m].id);
-        }
-    }
+    g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_apply_crtc, helper);
 
 #ifdef HAS_RANDR_ONE_POINT_THREE
         if (helper->has_1_3)
-            XRRSetOutputPrimary (xdisplay, GDK_WINDOW_XID (root_window), primary);
+            XRRSetOutputPrimary (helper->xdisplay, GDK_WINDOW_XID (helper->root_window),
+                                 helper->primary);
 #endif
 
     /* release the grab, changes are done */
-    gdk_x11_display_ungrab (display);
+    gdk_x11_display_ungrab (helper->display);
+    gdk_flush ();
+    gdk_error_trap_pop ();
+
+    /* refresh the cache (better safe than sorry) */
+    xfce_displays_helper_reload (helper);
 
 err_cleanup:
     /* Free the xfconf properties */
     if (saved_outputs)
         g_hash_table_destroy (saved_outputs);
-
-    /* Free our output cache */
-    g_ptr_array_foreach (connected_outputs, (GFunc) xfce_displays_helper_free_output, NULL);
-    g_ptr_array_free (connected_outputs, TRUE);
-
-    /* cleanup our CRTC cache */
-    for (m = 0; m < resources->ncrtc; ++m)
-    {
-        xfce_displays_helper_cleanup_crtc (&crtcs[m]);
-    }
-    g_free (crtcs);
-
-err_abort:
-    /* free the screen resources */
-    XRRFreeScreenResources (resources);
-
-    gdk_flush ();
-    gdk_error_trap_pop ();
 }
 
 


More information about the Xfce4-commits mailing list