Blob Blame History Raw
From 9fa99a888d9d9b84cabf419d321f0223e0d5ab7e Mon Sep 17 00:00:00 2001
From: Jonathon Jongsma <jjongsma@redhat.com>
Date: Mon, 17 Nov 2014 15:13:15 -0600
Subject: [PATCH 1/4] monitor-config: refactor make_default_config()

Move logic for creating different types of configurations into separate
functions. This keeps things a bit cleaner and allows us to add alternate
configuration types more easily.

Conflicts:
	src/backends/meta-monitor-config.c

https://bugzilla.redhat.com/show_bug.cgi?id=1166319
---
 src/backends/meta-monitor-config.c | 172 +++++++++++++++++++++----------------
 1 file changed, 100 insertions(+), 72 deletions(-)

diff --git a/src/backends/meta-monitor-config.c b/src/backends/meta-monitor-config.c
index 399e9f0..99042d7 100644
--- a/src/backends/meta-monitor-config.c
+++ b/src/backends/meta-monitor-config.c
@@ -1081,47 +1081,78 @@ find_primary_output (MetaOutput *outputs,
   return best;
 }
 
-static MetaConfiguration *
-make_default_config (MetaMonitorConfig *self,
-                     MetaOutput        *outputs,
-                     unsigned           n_outputs,
-                     int                max_width,
-                     int                max_height)
+static void
+init_config_from_preferred_mode (MetaOutputConfig *config,
+                                 MetaOutput *output)
+{
+  config->enabled = TRUE;
+  config->rect.x = 0;
+  config->rect.y = 0;
+  config->rect.width = output->preferred_mode->width;
+  config->rect.height = output->preferred_mode->height;
+  config->refresh_rate = output->preferred_mode->refresh_rate;
+  config->transform = META_MONITOR_TRANSFORM_NORMAL;
+  config->is_primary = FALSE;
+  config->is_presentation = FALSE;
+}
+
+static void
+make_linear_config (MetaMonitorConfig *self,
+                    MetaOutput        *outputs,
+                    unsigned           n_outputs,
+                    int                max_width,
+                    int                max_height,
+                    MetaConfiguration *config)
 {
-  unsigned i, j;
-  int x, y;
-  MetaConfiguration *ret;
   MetaOutput *primary;
+  unsigned i;
+  int x;
 
-  ret = g_slice_new (MetaConfiguration);
-  make_config_key (ret, outputs, n_outputs, -1);
-  ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
+  g_return_if_fail (config != NULL);
 
-  /* Special case the simple case: one output, primary at preferred mode,
-     nothing else to do */
-  if (n_outputs == 1)
+  primary = find_primary_output (outputs, n_outputs);
+
+  x = primary->preferred_mode->width;
+  for (i = 0; i < n_outputs; i++)
     {
-      ret->outputs[0].enabled = TRUE;
-      ret->outputs[0].rect.x = 0;
-      ret->outputs[0].rect.y = 0;
-      ret->outputs[0].rect.width = outputs[0].preferred_mode->width;
-      ret->outputs[0].rect.height = outputs[0].preferred_mode->height;
-      ret->outputs[0].refresh_rate = outputs[0].preferred_mode->refresh_rate;
-      ret->outputs[0].transform = META_MONITOR_TRANSFORM_NORMAL;
-      ret->outputs[0].is_primary = TRUE;
+      gboolean is_primary = (&outputs[i] == primary);
 
-      return ret;
-    }
+      init_config_from_preferred_mode (&config->outputs[i], &outputs[i]);
+      config->outputs[i].is_primary = is_primary;
 
-  /* If we reach this point, this is either the first time mutter runs
-     on this system ever, or we just hotplugged a new screen.
-     In the latter case, search for a configuration that includes one
-     less screen, then add the new one as a presentation screen
-     in preferred mode.
+      if (is_primary)
+        {
+          config->outputs[i].rect.x = 0;
+        }
+      else
+        {
+          config->outputs[i].rect.x = x;
+          x += config->outputs[i].rect.width;
+        }
+
+      /* Disable outputs that would go beyond framebuffer limits */
+      if (config->outputs[i].rect.x + config->outputs[i].rect.width > max_width)
+        config->outputs[i].enabled = FALSE;
+    }
+}
 
-     XXX: but presentation mode is not implemented in the control-center
-     or in mutter core, so let's do extended for now.
+/* Search for a configuration that includes one less screen, then add the new
+ * one as a presentation screen in preferred mode.
+ *
+ * XXX: but presentation mode is not implemented in the control-center or in
+ * mutter core, so let's do extended for now.
   */
+static gboolean
+extend_stored_config (MetaMonitorConfig *self,
+                      MetaOutput        *outputs,
+                      unsigned           n_outputs,
+                      int                max_width,
+                      int                max_height,
+                      MetaConfiguration *config)
+{
+  int x, y;
+  unsigned i, j;
+
   x = 0;
   y = 0;
   for (i = 0; i < n_outputs; i++)
@@ -1139,71 +1170,68 @@ make_default_config (MetaMonitorConfig *self,
             {
               if (j < i)
                 {
-                  g_assert (output_key_equal (&ret->keys[j], &ref->keys[j]));
-                  ret->outputs[j] = ref->outputs[j];
+                  g_assert (output_key_equal (&config->keys[j], &ref->keys[j]));
+                  config->outputs[j] = ref->outputs[j];
                   x = MAX (x, ref->outputs[j].rect.x + ref->outputs[j].rect.width);
                   y = MAX (y, ref->outputs[j].rect.y + ref->outputs[j].rect.height);
                 }
               else if (j > i)
                 {
-                  g_assert (output_key_equal (&ret->keys[j], &ref->keys[j - 1]));
-                  ret->outputs[j] = ref->outputs[j - 1];
+                  g_assert (output_key_equal (&config->keys[j], &ref->keys[j - 1]));
+                  config->outputs[j] = ref->outputs[j - 1];
                   x = MAX (x, ref->outputs[j - 1].rect.x + ref->outputs[j - 1].rect.width);
                   y = MAX (y, ref->outputs[j - 1].rect.y + ref->outputs[j - 1].rect.height);
                 }
               else
                 {
-                  ret->outputs[j].enabled = TRUE;
-                  ret->outputs[j].rect.x = 0;
-                  ret->outputs[j].rect.y = 0;
-                  ret->outputs[j].rect.width = outputs[0].preferred_mode->width;
-                  ret->outputs[j].rect.height = outputs[0].preferred_mode->height;
-                  ret->outputs[j].refresh_rate = outputs[0].preferred_mode->refresh_rate;
-                  ret->outputs[j].transform = META_MONITOR_TRANSFORM_NORMAL;
-                  ret->outputs[j].is_primary = FALSE;
-                  ret->outputs[j].is_presentation = FALSE;
+                  init_config_from_preferred_mode (&ref->outputs[j], &outputs[0]);
                 }
             }
 
           /* Place the new output at the right end of the screen, if it fits,
              otherwise below it, otherwise disable it (or apply_configuration will fail) */
-          if (x + ret->outputs[i].rect.width <= max_width)
-            ret->outputs[i].rect.x = x;
-          else if (y + ret->outputs[i].rect.height <= max_height)
-            ret->outputs[i].rect.y = y;
+          if (x + config->outputs[i].rect.width <= max_width)
+            config->outputs[i].rect.x = x;
+          else if (y + config->outputs[i].rect.height <= max_height)
+            config->outputs[i].rect.y = y;
           else
-            ret->outputs[i].enabled = FALSE;
+            config->outputs[i].enabled = FALSE;
 
-          return ret;
+          return TRUE;
         }
     }
 
-  /* No previous configuration found, try with a really default one, which
-     is one primary that goes first and the rest to the right of it, extended.
-  */
-  primary = find_primary_output (outputs, n_outputs);
+  return FALSE;
+}
 
-  x = primary->preferred_mode->width;
-  for (i = 0; i < n_outputs; i++)
-    {
-      MetaOutput *output = &outputs[i];
+static MetaConfiguration *
+make_default_config (MetaMonitorConfig *self,
+                     MetaOutput        *outputs,
+                     unsigned           n_outputs,
+                     int                max_width,
+                     int                max_height)
+{
+  MetaConfiguration *ret = NULL;
 
-      ret->outputs[i].enabled = TRUE;
-      ret->outputs[i].rect.x = (output == primary) ? 0 : x;
-      ret->outputs[i].rect.y = 0;
-      ret->outputs[i].rect.width = output->preferred_mode->width;
-      ret->outputs[i].rect.height = output->preferred_mode->height;
-      ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate;
-      ret->outputs[i].transform = META_MONITOR_TRANSFORM_NORMAL;
-      ret->outputs[i].is_primary = (output == primary);
+  ret = g_slice_new (MetaConfiguration);
+  make_config_key (ret, outputs, n_outputs, -1);
+  ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
 
-      /* Disable outputs that would go beyond framebuffer limits */
-      if (ret->outputs[i].rect.x + ret->outputs[i].rect.width > max_width)
-        ret->outputs[i].enabled = FALSE;
-      else if (output != primary)
-        x += output->preferred_mode->width;
+  /* Special case the simple case: one output, primary at preferred mode,
+     nothing else to do */
+  if (n_outputs == 1)
+    {
+
+      init_config_from_preferred_mode (&ret->outputs[0], &outputs[0]);
+      ret->outputs[0].is_primary = TRUE;
+      return ret;
     }
 
+  if (extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
+      return ret;
+
+  make_linear_config (self, outputs, n_outputs, max_width, max_height, ret);
+
   return ret;
 }
 
-- 
2.3.6


From 76566a956eaf7b905dbfd851ee63ebe6236ab872 Mon Sep 17 00:00:00 2001
From: Jonathon Jongsma <jjongsma@redhat.com>
Date: Wed, 12 Nov 2014 10:25:55 -0600
Subject: [PATCH 2/4] monitor-config: ignore stored config when
 hotplug_mode_update is set

When the output device has hotplug_mode_update (e.g. the qxl driver used in
vms), the displays can be dynamically resized, so the current display
configuration does not often match a stored configuration. When a new
monitor is added, make_default_config() tries to create a new display
configuration by choosing a stored configuration with N-1 monitors, and then
adding a new monitor to the end of the layout. Because the stored config
doesn't match the current outputs, apply_configuration() will routinely
fail, leaving the additional display unconfigured. In this case, it's more
useful to just fall back to creating a new default configuration from
scratch so that all outputs get configured to their preferred mode.

Conflicts:
	src/backends/meta-monitor-config.c

https://bugzilla.redhat.com/show_bug.cgi?id=1166319
---
 src/backends/meta-monitor-config.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/backends/meta-monitor-config.c b/src/backends/meta-monitor-config.c
index 99042d7..3e57215 100644
--- a/src/backends/meta-monitor-config.c
+++ b/src/backends/meta-monitor-config.c
@@ -1209,7 +1209,8 @@ make_default_config (MetaMonitorConfig *self,
                      MetaOutput        *outputs,
                      unsigned           n_outputs,
                      int                max_width,
-                     int                max_height)
+                     int                max_height,
+                     gboolean           use_stored_config)
 {
   MetaConfiguration *ret = NULL;
 
@@ -1227,7 +1228,8 @@ make_default_config (MetaMonitorConfig *self,
       return ret;
     }
 
-  if (extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
+  if (use_stored_config &&
+      extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
       return ret;
 
   make_linear_config (self, outputs, n_outputs, max_width, max_height, ret);
@@ -1292,6 +1294,7 @@ meta_monitor_config_make_default (MetaMonitorConfig  *self,
   unsigned n_outputs;
   gboolean ok;
   int max_width, max_height;
+  gboolean use_stored_config;
 
   outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
   meta_monitor_manager_get_screen_limits (manager, &max_width, &max_height);
@@ -1302,7 +1305,15 @@ meta_monitor_config_make_default (MetaMonitorConfig  *self,
       return;
     }
 
-  default_config = make_default_config (self, outputs, n_outputs, max_width, max_height);
+  /* if the device has hotplug_mode_update, it's possible that the
+   * current display configuration does not match a stored configuration.
+   * Since extend_existing_config() tries to build a configuration that is
+   * based on a previously-stored configuration, it's quite likely that the
+   * resulting config will fail. Even if it doesn't fail, it may result in
+   * an unexpected configuration, so don't attempt to use a stored config
+   * in this situation. */
+  use_stored_config = !meta_monitor_manager_has_hotplug_mode_update (manager);
+  default_config = make_default_config (self, outputs, n_outputs, max_width, max_height, use_stored_config);
 
   if (default_config != NULL)
     {
-- 
2.3.6


From f357ed24e24efa26b2d65ca307db8c20e20238b9 Mon Sep 17 00:00:00 2001
From: Jonathon Jongsma <jjongsma@redhat.com>
Date: Mon, 10 Nov 2014 15:36:47 -0600
Subject: [PATCH 3/4] monitor-manager: Add support for suggested position for
 outputs

In recent versions of the QXL driver, it may set "suggested X|Y" connector
properties. These properties are used to indicate the position at which
multiple displays should be aligned.  If all outputs have a suggested position,
the displays are arranged according to these positions, otherwise we fall back
to the default configuration.

At the moment, we trust that the driver has chosen sane values for the
suggested position.

https://bugzilla.redhat.com/show_bug.cgi?id=1166319
---
 src/backends/meta-monitor-config.c             | 53 ++++++++++++++++++++++++++
 src/backends/meta-monitor-manager.h            |  2 +
 src/backends/native/meta-monitor-manager-kms.c |  2 +
 src/backends/x11/meta-monitor-manager-xrandr.c | 52 +++++++++++++++++++++++++
 4 files changed, 109 insertions(+)

diff --git a/src/backends/meta-monitor-config.c b/src/backends/meta-monitor-config.c
index 3e57215..a65c241 100644
--- a/src/backends/meta-monitor-config.c
+++ b/src/backends/meta-monitor-config.c
@@ -34,6 +34,7 @@
 
 #include "config.h"
 
+#include "boxes-private.h"
 #include "meta-monitor-config.h"
 
 #include <string.h>
@@ -1096,6 +1097,55 @@ init_config_from_preferred_mode (MetaOutputConfig *config,
   config->is_presentation = FALSE;
 }
 
+/* This function handles configuring the outputs when the driver provides a
+ * suggested layout position for each output. This is done in recent versions
+ * of qxl and allows displays to be aligned on the guest in the same order as
+ * they are aligned on the client.
+ */
+static gboolean
+make_suggested_config (MetaMonitorConfig *self,
+                       MetaOutput        *outputs,
+                       unsigned           n_outputs,
+                       int                max_width,
+                       int                max_height,
+                       MetaConfiguration *config)
+{
+  unsigned int i;
+  MetaOutput *primary;
+  GList *region = NULL;
+
+  g_return_val_if_fail (config != NULL, FALSE);
+  primary = find_primary_output (outputs, n_outputs);
+
+  for (i = 0; i < n_outputs; i++)
+    {
+      gboolean is_primary = (&outputs[i] == primary);
+
+      if (outputs[i].suggested_x < 0 || outputs[i].suggested_y < 0)
+          return FALSE;
+
+      init_config_from_preferred_mode (&config->outputs[i], &outputs[i]);
+      config->outputs[i].is_primary = is_primary;
+
+      config->outputs[i].rect.x = outputs[i].suggested_x;
+      config->outputs[i].rect.y = outputs[i].suggested_y;
+
+      /* Reject the configuration if the suggested positions result in
+       * overlapping displays */
+      if (meta_rectangle_overlaps_with_region (region, &config->outputs[i].rect))
+        {
+          g_warning ("Overlapping outputs, rejecting suggested configuration");
+          g_list_free (region);
+          return FALSE;
+        }
+
+      region = g_list_prepend (region, &config->outputs[i].rect);
+    }
+
+  g_list_free (region);
+  return TRUE;
+}
+
 static void
 make_linear_config (MetaMonitorConfig *self,
                     MetaOutput        *outputs,
@@ -1228,6 +1278,9 @@ make_default_config (MetaMonitorConfig *self,
       return ret;
     }
 
+  if (make_suggested_config (self, outputs, n_outputs, max_width, max_height, ret))
+      return ret;
+
   if (use_stored_config &&
       extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
       return ret;
diff --git a/src/backends/meta-monitor-manager.h b/src/backends/meta-monitor-manager.h
index 77ab938..0b81a4e 100644
--- a/src/backends/meta-monitor-manager.h
+++ b/src/backends/meta-monitor-manager.h
@@ -116,6 +116,8 @@ struct _MetaOutput
 
   /* get a new preferred mode on hotplug events, to handle dynamic guest resizing */
   gboolean hotplug_mode_update;
+  gint suggested_x;
+  gint suggested_y;
 };
 
 struct _MetaCRTC
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index ad37df6..2e46276 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -410,6 +410,8 @@ meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
 	  meta_output->name = make_output_name (connector);
 	  meta_output->width_mm = connector->mmWidth;
 	  meta_output->height_mm = connector->mmHeight;
+	  meta_output->suggested_x = -1;
+	  meta_output->suggested_y = -1;
 
           switch (connector->subpixel)
             {
diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c
index 4c1b16c..2c6d65e 100644
--- a/src/backends/x11/meta-monitor-manager-xrandr.c
+++ b/src/backends/x11/meta-monitor-manager-xrandr.c
@@ -141,6 +141,34 @@ meta_monitor_transform_from_xrandr_all (Rotation rotation)
 }
 
 static gboolean
+output_get_integer_property (MetaMonitorManagerXrandr *manager_xrandr,
+                             MetaOutput *output, const char *propname,
+                             gint *value)
+{
+  gboolean exists = FALSE;
+  Atom atom, actual_type;
+  int actual_format;
+  unsigned long nitems, bytes_after;
+  unsigned char *buffer;
+
+  atom = XInternAtom (manager_xrandr->xdisplay, propname, False);
+  XRRGetOutputProperty (manager_xrandr->xdisplay,
+                        (XID)output->winsys_id,
+                        atom,
+                        0, G_MAXLONG, False, False, XA_INTEGER,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  exists = (actual_type == XA_INTEGER && actual_format == 32 && nitems == 1);
+
+  if (exists && value != NULL)
+    *value = ((int*)buffer)[0];
+
+  XFree (buffer);
+  return exists;
+}
+
+static gboolean
 output_get_property_exists (MetaMonitorManagerXrandr *manager_xrandr,
                             MetaOutput *output, const char *propname)
 {
@@ -357,6 +385,28 @@ output_get_hotplug_mode_update (MetaMonitorManagerXrandr *manager_xrandr,
   return output_get_property_exists (manager_xrandr, output, "hotplug_mode_update");
 }
 
+static gint
+output_get_suggested_x (MetaMonitorManagerXrandr *manager_xrandr,
+                        MetaOutput               *output)
+{
+  gint val;
+  if (output_get_integer_property (manager_xrandr, output, "suggested X", &val))
+    return val;
+
+  return -1;
+}
+
+static gint
+output_get_suggested_y (MetaMonitorManagerXrandr *manager_xrandr,
+                        MetaOutput               *output)
+{
+  gint val;
+  if (output_get_integer_property (manager_xrandr, output, "suggested Y", &val))
+    return val;
+
+  return -1;
+}
+
 static char *
 get_xmode_name (XRRModeInfo *xmode)
 {
@@ -545,6 +595,8 @@ meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
 	  meta_output->height_mm = output->mm_height;
 	  meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
           meta_output->hotplug_mode_update = output_get_hotplug_mode_update (manager_xrandr, meta_output);
+	  meta_output->suggested_x = output_get_suggested_x (manager_xrandr, meta_output);
+	  meta_output->suggested_y = output_get_suggested_y (manager_xrandr, meta_output);
 
 	  meta_output->n_modes = output->nmode;
 	  meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
-- 
2.3.6


From d0607177b47d2e499ec822fb7aa4c55224f4c85a Mon Sep 17 00:00:00 2001
From: Jonathon Jongsma <jjongsma@redhat.com>
Date: Wed, 19 Nov 2014 10:56:27 -0600
Subject: [PATCH 4/4] monitor-manager: check framebuffer limits for all configs

Refactor make_default_config() to always sanity-check the configuration to
ensure that it fits within the framebuffer. Previously, this was only done
for the default linear configuration.

https://bugzilla.redhat.com/show_bug.cgi?id=1166319
---
 src/backends/meta-monitor-config.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/backends/meta-monitor-config.c b/src/backends/meta-monitor-config.c
index a65c241..9e15989 100644
--- a/src/backends/meta-monitor-config.c
+++ b/src/backends/meta-monitor-config.c
@@ -1179,10 +1179,6 @@ make_linear_config (MetaMonitorConfig *self,
           config->outputs[i].rect.x = x;
           x += config->outputs[i].rect.width;
         }
-
-      /* Disable outputs that would go beyond framebuffer limits */
-      if (config->outputs[i].rect.x + config->outputs[i].rect.width > max_width)
-        config->outputs[i].enabled = FALSE;
     }
 }
 
@@ -1263,6 +1259,7 @@ make_default_config (MetaMonitorConfig *self,
                      gboolean           use_stored_config)
 {
   MetaConfiguration *ret = NULL;
+  unsigned i;
 
   ret = g_slice_new (MetaConfiguration);
   make_config_key (ret, outputs, n_outputs, -1);
@@ -1272,21 +1269,29 @@ make_default_config (MetaMonitorConfig *self,
      nothing else to do */
   if (n_outputs == 1)
     {
-
       init_config_from_preferred_mode (&ret->outputs[0], &outputs[0]);
       ret->outputs[0].is_primary = TRUE;
-      return ret;
+      goto check_limits;
     }
 
   if (make_suggested_config (self, outputs, n_outputs, max_width, max_height, ret))
-      return ret;
+      goto check_limits;
 
   if (use_stored_config &&
       extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
-      return ret;
+      goto check_limits;
 
   make_linear_config (self, outputs, n_outputs, max_width, max_height, ret);
 
+check_limits:
+  /* Disable outputs that would go beyond framebuffer limits */
+  for (i = 0; i < n_outputs; i++)
+    {
+        if ((ret->outputs[i].rect.x + ret->outputs[i].rect.width > max_width)
+            || (ret->outputs[i].rect.y + ret->outputs[i].rect.height > max_height))
+          ret->outputs[i].enabled = FALSE;
+    }
+
   return ret;
 }
 
-- 
2.3.6