From af101f7fbcef2034989896a2ad185ccb049a3513 Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <federico@novell.com>
Date: Wed, 9 Dec 2009 15:59:11 -0600
Subject: [PATCH 1/4] bgo#590823 - RANDR - Automatically turn on outputs which support Mirror Screens

Previously, outputs could remain off if the user just selected Mirror Screens.
Now we automatically turn on all the outputs which support the clone mode,
to avoid extra work on the part of the user.

Signed-off-by: Federico Mena Quintero <federico@novell.com>
---
 capplets/display/xrandr-capplet.c |   79 +++++++++++++++++++++++++++++++++++++
 1 files changed, 79 insertions(+), 0 deletions(-)

Index: gnome-control-center-2.28.1/capplets/display/xrandr-capplet.c
===================================================================
--- gnome-control-center-2.28.1.orig/capplets/display/xrandr-capplet.c
+++ gnome-control-center-2.28.1/capplets/display/xrandr-capplet.c
@@ -80,12 +80,15 @@ struct App
 };
 
 static void rebuild_gui (App *app);
+static void on_clone_changed (GtkWidget *box, gpointer data);
 static void on_rate_changed (GtkComboBox *box, gpointer data);
 static gboolean output_overlaps (GnomeOutputInfo *output, GnomeRRConfig *config);
 static void select_current_output_from_dialog_position (App *app);
 static void monitor_on_off_toggled_cb (GtkToggleButton *toggle, gpointer data);
 static void get_geometry (GnomeOutputInfo *output, int *w, int *h);
 static void apply_configuration_returned_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, void *data);
+static gboolean get_clone_size (GnomeRRScreen *screen, int *width, int *height);
+static gboolean output_info_supports_mode (App *app, GnomeOutputInfo *info, int width, int height);
 
 #define ROTATE_TABLET_KEY "/apps/gnome_settings_daemon/xrandr/rotate_tablet_with_monitor"
 
@@ -424,6 +427,67 @@ count_all_outputs (GnomeRRConfig *config
 }
 #endif
 
+/* Computes whether "Mirror Screens" (clone mode) is supported based on these criteria:
+ *
+ * 1. There is an available size for cloning.
+ *
+ * 2. There are 2 or more connected outputs that support that size.
+ */
+static gboolean
+mirror_screens_is_supported (App *app)
+{
+    int clone_width, clone_height;
+    gboolean have_clone_size;
+    gboolean mirror_is_supported;
+
+    mirror_is_supported = FALSE;
+
+    have_clone_size = get_clone_size (app->screen, &clone_width, &clone_height);
+
+    if (have_clone_size) {
+	int i;
+	int num_outputs_with_clone_size;
+
+	num_outputs_with_clone_size = 0;
+
+	for (i = 0; app->current_configuration->outputs[i] != NULL; i++)
+	{
+	    GnomeOutputInfo *output = app->current_configuration->outputs[i];
+
+	    /* We count the connected outputs that support the clone size.  It
+	     * doesn't matter if those outputs aren't actually On currently; we
+	     * will turn them on in on_clone_changed().
+	     */
+	    if (output->connected && output_info_supports_mode (app, output, clone_width, clone_height))
+		num_outputs_with_clone_size++;
+	}
+
+	if (num_outputs_with_clone_size >= 2)
+	    mirror_is_supported = TRUE;
+    }
+
+    return mirror_is_supported;
+}
+
+static void
+rebuild_mirror_screens (App *app)
+{
+    gboolean mirror_is_active;
+    gboolean mirror_is_supported;
+
+    g_signal_handlers_block_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app);
+
+    mirror_is_active = app->current_configuration && app->current_configuration->clone;
+
+    /* If mirror_is_active, then it *must* be possible to turn mirroring off */
+    mirror_is_supported = mirror_is_active || mirror_screens_is_supported (app);
+    
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), mirror_is_active);
+    gtk_widget_set_sensitive (app->clone_checkbox, mirror_is_supported);
+
+    g_signal_handlers_unblock_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app);
+}
+
 static void
 rebuild_current_monitor_label (App *app)
 {
@@ -433,7 +497,11 @@ rebuild_current_monitor_label (App *app)
 
 	if (app->current_output)
 	{
-	    tmp = g_strdup_printf (_("Monitor: %s"), app->current_output->display_name);
+	    if (app->current_configuration->clone)
+		tmp = g_strdup (_("Mirror Screens"));
+	    else
+		tmp = g_strdup_printf (_("Monitor: %s"), app->current_output->display_name);
+
 	    str = g_strdup_printf ("<b>%s</b>", tmp);
 	    gnome_rr_labeler_get_color_for_output (app->labeler, app->current_output, &color);
 	    use_color = TRUE;
@@ -482,7 +550,11 @@ rebuild_on_off_radios (App *app)
     g_signal_handlers_block_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
     g_signal_handlers_block_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
 
-    if (app->current_output)
+    sensitive = FALSE;
+    on_active = FALSE;
+    off_active = FALSE;
+
+    if (!app->current_configuration->clone && app->current_output)
     {
 	if (count_active_outputs (app) > 1 || !app->current_output->on)
 	    sensitive = TRUE;
@@ -492,12 +564,6 @@ rebuild_on_off_radios (App *app)
 	on_active = app->current_output->on;
 	off_active = !on_active;
     }
-    else
-    {
-	sensitive = FALSE;
-	on_active = FALSE;
-	off_active = FALSE;
-    }
 
     gtk_widget_set_sensitive (app->monitor_on_radio, sensitive);
     gtk_widget_set_sensitive (app->monitor_off_radio, sensitive);
@@ -601,6 +667,7 @@ rebuild_gui (App *app)
     g_debug ("rebuild gui, is on: %d", app->current_output->on);
 #endif
 
+    rebuild_mirror_screens (app);
     rebuild_current_monitor_label (app);
     rebuild_on_off_radios (app);
     rebuild_resolution_combo (app);
@@ -613,11 +680,6 @@ rebuild_gui (App *app)
     gtk_widget_set_sensitive (app->panel_checkbox, sensitive);
 
     app->ignore_gui_changes = FALSE;
-
-    if (app->current_configuration && app->current_configuration->clone)
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), TRUE);
-    else
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), FALSE);
 }
 
 static gboolean
@@ -862,6 +924,69 @@ lay_out_outputs_horizontally (App *app)
 
 }
 
+/* FIXME: this function is copied from gnome-settings-daemon/plugins/xrandr/gsd-xrandr-manager.c.
+ * Do we need to put this function in gnome-desktop for public use?
+ */
+static gboolean
+get_clone_size (GnomeRRScreen *screen, int *width, int *height)
+{
+        GnomeRRMode **modes = gnome_rr_screen_list_clone_modes (screen);
+        int best_w, best_h;
+        int i;
+
+        best_w = 0;
+        best_h = 0;
+
+        for (i = 0; modes[i] != NULL; ++i) {
+                GnomeRRMode *mode = modes[i];
+                int w, h;
+
+                w = gnome_rr_mode_get_width (mode);
+                h = gnome_rr_mode_get_height (mode);
+
+                if (w * h > best_w * best_h) {
+                        best_w = w;
+                        best_h = h;
+                }
+        }
+
+        if (best_w > 0 && best_h > 0) {
+                if (width)
+                        *width = best_w;
+                if (height)
+                        *height = best_h;
+
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+output_info_supports_mode (App *app, GnomeOutputInfo *info, int width, int height)
+{
+    GnomeRROutput *output;
+    GnomeRRMode **modes;
+    int i;
+
+    if (!info->connected)
+	return FALSE;
+
+    output = gnome_rr_screen_get_output_by_name (app->screen, info->name);
+    if (!output)
+	return FALSE;
+
+    modes = gnome_rr_output_list_modes (output);
+
+    for (i = 0; modes[i]; i++) {
+	if (gnome_rr_mode_get_width (modes[i]) == width
+	    && gnome_rr_mode_get_height (modes[i]) == height)
+	    return TRUE;
+    }
+
+    return FALSE;
+}
+
 static void
 on_clone_changed (GtkWidget *box, gpointer data)
 {
@@ -873,6 +998,7 @@ on_clone_changed (GtkWidget *box, gpoint
     if (app->current_configuration->clone)
     {
 	int i;
+	int width, height;
 
 	for (i = 0; app->current_configuration->outputs[i]; ++i)
 	{
@@ -882,6 +1008,21 @@ on_clone_changed (GtkWidget *box, gpoint
 		break;
 	    }
 	}
+
+	/* Turn on all the connected screens that support the best clone mode.
+	 * The user may hit "Mirror Screens", but he shouldn't have to turn on
+	 * all the required outputs as well.
+	 */
+
+	get_clone_size (app->screen, &width, &height);
+
+	for (i = 0; app->current_configuration->outputs[i]; i++) {
+	    if (output_info_supports_mode (app, app->current_configuration->outputs[i], width, height)) {
+		app->current_configuration->outputs[i]->on = TRUE;
+		app->current_configuration->outputs[i]->width = width;
+		app->current_configuration->outputs[i]->height = height;
+	    }
+	}
     }
     else
     {
