[mutter] backends: Add API to switch to predetermined monitor configurations



commit 3f9c5823cbb976e4cc1c56f39cea7f81469c8349
Author: Rui Matos <tiagomatos gmail com>
Date:   Wed Jun 7 18:38:10 2017 +0200

    backends: Add API to switch to predetermined monitor configurations
    
    This will allows us to support the XF86Display key present on some
    laptops, directly in mutter. This is also known, in evdev, as
    KEY_SWITCHVIDEOMODE.
    
    The common usage for this key is to alternate between a few well known
    multi-monitor configurations though these aren't officially
    standardized. As an example, Lenovo documents it as:
    
    "Switches the display output location between the computer display
    and an external monitor."
    
    On this patch, we're just introducing the configurations that have been
    implemented in g-s-d until now, which go a bit beyond the above
    description.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=781906

 src/backends/meta-monitor-config-manager.c  |  199 ++++++++++++++++++++++++++
 src/backends/meta-monitor-config-manager.h  |    3 +
 src/backends/meta-monitor-config.c          |  201 ++++++++++++++++++++++++++-
 src/backends/meta-monitor-config.h          |    3 +
 src/backends/meta-monitor-manager-private.h |    2 +
 src/backends/meta-monitor-manager.c         |   53 +++++++
 src/meta/meta-monitor-manager.h             |   16 ++
 7 files changed, 474 insertions(+), 3 deletions(-)
---
diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
index 91ee2ba..6180b4e 100644
--- a/src/backends/meta-monitor-config-manager.c
+++ b/src/backends/meta-monitor-config-manager.c
@@ -751,6 +751,205 @@ meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager
   return create_for_builtin_display_rotation (config_manager, TRUE, META_MONITOR_TRANSFORM_NORMAL);
 }
 
+static MetaMonitorsConfig *
+create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager)
+{
+  MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
+  MetaLogicalMonitorConfig *logical_monitor_config = NULL;
+  GList *monitor_configs = NULL;
+  gint common_mode_w = 0, common_mode_h = 0;
+  float best_scale = 1.0;
+  MetaMonitor *monitor;
+  GList *modes;
+  GList *monitors;
+  GList *l;
+
+  monitors = meta_monitor_manager_get_monitors (monitor_manager);
+  monitor = monitors->data;
+  modes = meta_monitor_get_modes (monitor);
+  for (l = modes; l; l = l->next)
+    {
+      MetaMonitorMode *mode = l->data;
+      gboolean common_mode_size = TRUE;
+      gint mode_w, mode_h;
+      GList *ll;
+
+      meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h);
+
+      for (ll = monitors->next; ll; ll = ll->next)
+        {
+          MetaMonitor *monitor_b = ll->data;
+          gboolean have_same_mode_size = FALSE;
+          GList *mm;
+
+          for (mm = meta_monitor_get_modes (monitor_b); mm; mm = mm->next)
+            {
+              MetaMonitorMode *mode_b = mm->data;
+              gint mode_b_w, mode_b_h;
+
+              meta_monitor_mode_get_resolution (mode_b, &mode_b_w, &mode_b_h);
+
+              if (mode_w == mode_b_w &&
+                  mode_h == mode_b_h)
+                {
+                  have_same_mode_size = TRUE;
+                  break;
+                }
+            }
+
+          if (!have_same_mode_size)
+            {
+              common_mode_size = FALSE;
+              break;
+            }
+        }
+
+      if (common_mode_size &&
+          common_mode_w * common_mode_h < mode_w * mode_h)
+        {
+          common_mode_w = mode_w;
+          common_mode_h = mode_h;
+        }
+    }
+
+  if (common_mode_w == 0 || common_mode_h == 0)
+    return NULL;
+
+  for (l = monitors; l; l = l->next)
+    {
+      MetaMonitor *monitor = l->data;
+      MetaMonitorMode *mode = NULL;
+      GList *ll;
+      float scale;
+
+      for (ll = meta_monitor_get_modes (monitor); ll; ll = ll->next)
+        {
+          gint mode_w, mode_h;
+
+          mode = ll->data;
+          meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h);
+
+          if (mode_w == common_mode_w && mode_h == common_mode_h)
+            break;
+        }
+
+      if (!mode)
+        continue;
+
+      scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, monitor, mode);
+      best_scale = MAX (best_scale, scale);
+      monitor_configs = g_list_prepend (monitor_configs, create_monitor_config (monitor, mode));
+    }
+
+  logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
+  *logical_monitor_config = (MetaLogicalMonitorConfig) {
+    .layout = (MetaRectangle) {
+      .x = 0,
+      .y = 0,
+      .width = common_mode_w,
+      .height = common_mode_h
+    },
+    .scale = best_scale,
+    .monitor_configs = monitor_configs
+  };
+
+  return meta_monitors_config_new (g_list_append (NULL, logical_monitor_config),
+                                   meta_monitor_manager_get_default_layout_mode (monitor_manager));
+}
+
+static MetaMonitorsConfig *
+create_for_switch_config_external (MetaMonitorConfigManager *config_manager)
+{
+  MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
+  GList *logical_monitor_configs = NULL;
+  int x = 0;
+  MetaLogicalMonitorLayoutMode layout_mode;
+  GList *monitors;
+  GList *l;
+
+  layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
+
+  monitors = meta_monitor_manager_get_monitors (monitor_manager);
+  for (l = monitors; l; l = l->next)
+    {
+      MetaMonitor *monitor = l->data;
+      MetaLogicalMonitorConfig *logical_monitor_config;
+
+      if (meta_monitor_is_laptop_panel (monitor))
+        continue;
+
+      logical_monitor_config =
+        create_preferred_logical_monitor_config (monitor_manager,
+                                                 monitor,
+                                                 x, 0,
+                                                 NULL,
+                                                 layout_mode);
+      logical_monitor_configs = g_list_append (logical_monitor_configs,
+                                               logical_monitor_config);
+
+      if (x == 0)
+        logical_monitor_config->is_primary = TRUE;
+
+      x += logical_monitor_config->layout.width;
+    }
+
+  return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+}
+
+static MetaMonitorsConfig *
+create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager)
+{
+  MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
+  MetaLogicalMonitorLayoutMode layout_mode;
+  GList *logical_monitor_configs;
+  MetaLogicalMonitorConfig *primary_logical_monitor_config;
+  MetaMonitor *monitor;
+
+  monitor = meta_monitor_manager_get_laptop_panel (monitor_manager);
+  if (!monitor)
+    return NULL;
+
+  layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
+
+  primary_logical_monitor_config =
+    create_preferred_logical_monitor_config (monitor_manager,
+                                             monitor,
+                                             0, 0,
+                                             NULL,
+                                             layout_mode);
+  primary_logical_monitor_config->is_primary = TRUE;
+  logical_monitor_configs = g_list_append (NULL,
+                                           primary_logical_monitor_config);
+
+  return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+}
+
+MetaMonitorsConfig *
+meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager    *config_manager,
+                                                      MetaMonitorSwitchConfigType  config_type)
+{
+  MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
+
+  if (!meta_monitor_manager_can_switch_config (monitor_manager))
+    return NULL;
+
+  switch (config_type)
+    {
+    case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR:
+      return create_for_switch_config_all_mirror (config_manager);
+    case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR:
+      return meta_monitor_config_manager_create_linear (config_manager);
+    case META_MONITOR_SWITCH_CONFIG_EXTERNAL:
+      return create_for_switch_config_external (config_manager);
+    case META_MONITOR_SWITCH_CONFIG_BUILTIN:
+      return create_for_switch_config_builtin (config_manager);
+    case META_MONITOR_SWITCH_CONFIG_UNKNOWN:
+      g_warn_if_reached ();
+      break;
+    }
+  return NULL;
+}
+
 void
 meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager,
                                          MetaMonitorsConfig       *config)
diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
index c79084d..8f6f3dc 100644
--- a/src/backends/meta-monitor-config-manager.h
+++ b/src/backends/meta-monitor-config-manager.h
@@ -88,6 +88,9 @@ MetaMonitorsConfig * meta_monitor_config_manager_create_for_orientation (MetaMon
 
 MetaMonitorsConfig * meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager 
*config_manager);
 
+MetaMonitorsConfig * meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager    
*config_manager,
+                                                                           MetaMonitorSwitchConfigType  
config_type);
+
 void meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager,
                                               MetaMonitorsConfig       *config);
 
diff --git a/src/backends/meta-monitor-config.c b/src/backends/meta-monitor-config.c
index b527857..3ef3c62 100644
--- a/src/backends/meta-monitor-config.c
+++ b/src/backends/meta-monitor-config.c
@@ -1270,9 +1270,9 @@ make_linear_config (MetaMonitorConfig *self,
                     unsigned           n_outputs,
                     int                max_width,
                     int                max_height,
-                    MetaConfiguration *config)
+                    MetaConfiguration *config,
+                    unsigned long      output_configured_bitmap)
 {
-  unsigned long output_configured_bitmap = 0;
   unsigned i;
   int x;
   int primary;
@@ -1409,7 +1409,7 @@ make_default_config (MetaMonitorConfig *self,
       extend_stored_config (self, outputs, n_outputs, max_width, max_height, ret))
       goto check_limits;
 
-  make_linear_config (self, outputs, n_outputs, max_width, max_height, ret);
+  make_linear_config (self, outputs, n_outputs, max_width, max_height, ret, 0);
 
 check_limits:
   /* Disable outputs that would go beyond framebuffer limits */
@@ -1675,6 +1675,201 @@ meta_monitor_config_rotate_monitor (MetaMonitorConfig *self)
   do_builtin_display_rotation (self, TRUE, META_MONITOR_TRANSFORM_NORMAL);
 }
 
+static MetaConfiguration *
+make_all_mirror_config (MetaMonitorConfig *self,
+                        MetaOutput        *outputs,
+                        guint              n_outputs)
+{
+  MetaConfiguration *config;
+  gint common_width = 0;
+  gint common_height = 0;
+  guint i, j, k;
+
+  if (n_outputs < 2)
+    return NULL;
+
+  for (i = 0; i < outputs[0].n_modes; i++)
+    {
+      gboolean common_mode_size = TRUE;
+
+      for (j = 1; j < n_outputs; j++)
+        {
+          gboolean have_same_mode_size = FALSE;
+
+          for (k = 0; k < outputs[j].n_modes; k++)
+            {
+              if (outputs[j].modes[k]->width == outputs[0].modes[i]->width &&
+                  outputs[j].modes[k]->height == outputs[0].modes[i]->height)
+                {
+                  have_same_mode_size = TRUE;
+                  break;
+                }
+            }
+
+          if (!have_same_mode_size)
+            {
+              common_mode_size = FALSE;
+              break;
+            }
+        }
+
+      if (common_mode_size &&
+          common_width * common_height < outputs[0].modes[i]->width * outputs[0].modes[i]->height)
+        {
+          common_width = outputs[0].modes[i]->width;
+          common_height = outputs[0].modes[i]->height;
+        }
+    }
+
+  if (common_width == 0 || common_height == 0)
+    return NULL;
+
+  config = config_new ();
+  make_config_key (config, outputs, n_outputs, -1);
+  config->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+  for (i = 0; i < n_outputs; i++)
+    {
+      init_config_from_preferred_mode (&config->outputs[i], &outputs[i]);
+      config->outputs[i].rect.width = common_width;
+      config->outputs[i].rect.height = common_height;
+      config->outputs[i].is_primary = TRUE;
+    }
+
+  return config;
+}
+
+static MetaConfiguration *
+make_all_linear_config (MetaMonitorConfig *self,
+                        MetaOutput        *outputs,
+                        guint              n_outputs,
+                        gint               max_width,
+                        gint               max_height)
+{
+  MetaConfiguration *config;
+
+  config = config_new ();
+  make_config_key (config, outputs, n_outputs, -1);
+  config->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+  make_linear_config (self, outputs, n_outputs, max_width, max_height, config, 0);
+
+  return config;
+}
+
+static MetaConfiguration *
+make_external_config (MetaMonitorConfig *self,
+                      MetaOutput        *outputs,
+                      guint              n_outputs,
+                      gint               max_width,
+                      gint               max_height)
+{
+  MetaConfiguration *config;
+  gulong bitmap;
+  guint i;
+
+  config = config_new ();
+  make_config_key (config, outputs, n_outputs, -1);
+  config->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+  bitmap = 0;
+  for (i = 0; i < n_outputs; i++)
+    if (meta_output_is_laptop (&outputs[i]))
+      {
+        bitmap = 1 << i;
+        break;
+      }
+
+  make_linear_config (self, outputs, n_outputs, max_width, max_height, config, bitmap);
+
+  return config;
+}
+
+static MetaConfiguration *
+make_builtin_config (MetaMonitorConfig *self,
+                     MetaOutput        *outputs,
+                     guint              n_outputs)
+{
+  MetaConfiguration *config;
+  gboolean have_builtin = FALSE;
+  guint i;
+
+  config = config_new ();
+  make_config_key (config, outputs, n_outputs, -1);
+  config->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+  for (i = 0; i < n_outputs; i++)
+    {
+      if (meta_output_is_laptop (&outputs[i]))
+        {
+          init_config_from_preferred_mode (&config->outputs[i], &outputs[i]);
+          config->outputs[i].is_primary = TRUE;
+          have_builtin = TRUE;
+        }
+      else
+        {
+          config->outputs[i].enabled = FALSE;
+        }
+    }
+
+  if (have_builtin)
+    return config;
+
+  config_unref (config);
+  return NULL;
+}
+
+gboolean
+meta_monitor_config_switch_config (MetaMonitorConfig           *self,
+                                   MetaMonitorSwitchConfigType  config_type)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend);
+  MetaConfiguration *new_config = NULL;
+  MetaOutput *outputs;
+  gint max_width, max_height;
+  guint n_outputs;
+  gboolean success;
+
+  if (!meta_monitor_manager_can_switch_config (monitor_manager))
+    return FALSE;
+
+  outputs = meta_monitor_manager_get_outputs (monitor_manager, &n_outputs);
+
+  if (!meta_monitor_manager_get_max_screen_size (monitor_manager, &max_width, &max_height))
+    {
+      max_width = 65535;
+      max_height = 65535;
+    }
+
+  switch (config_type)
+    {
+    case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR:
+      new_config = make_all_mirror_config (self, outputs, n_outputs);
+      break;
+    case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR:
+      new_config = make_all_linear_config (self, outputs, n_outputs, max_width, max_height);
+      break;
+    case META_MONITOR_SWITCH_CONFIG_EXTERNAL:
+      new_config = make_external_config (self, outputs, n_outputs, max_width, max_height);
+      break;
+    case META_MONITOR_SWITCH_CONFIG_BUILTIN:
+      new_config = make_builtin_config (self, outputs, n_outputs);
+      break;
+    case META_MONITOR_SWITCH_CONFIG_UNKNOWN:
+      g_warn_if_reached ();
+      break;
+    }
+
+  if (!new_config)
+    return FALSE;
+
+  success = apply_configuration (self, new_config, monitor_manager);
+  config_unref (new_config);
+
+  return success;
+}
+
 typedef struct {
   MetaMonitorConfig *config;
   GString *buffer;
diff --git a/src/backends/meta-monitor-config.h b/src/backends/meta-monitor-config.h
index 4199570..86450dc 100644
--- a/src/backends/meta-monitor-config.h
+++ b/src/backends/meta-monitor-config.h
@@ -54,4 +54,7 @@ void               meta_monitor_config_orientation_changed (MetaMonitorConfig
 
 void               meta_monitor_config_rotate_monitor (MetaMonitorConfig *self);
 
+gboolean           meta_monitor_config_switch_config (MetaMonitorConfig           *self,
+                                                      MetaMonitorSwitchConfigType  config_type);
+
 #endif /* META_MONITOR_CONFIG_H */
diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h
index 9fed7cf..3ba74f3 100644
--- a/src/backends/meta-monitor-manager-private.h
+++ b/src/backends/meta-monitor-manager-private.h
@@ -354,6 +354,8 @@ struct _MetaMonitorManager
   UpClient *up_client;
 
   gulong experimental_features_changed_handler_id;
+
+  MetaMonitorSwitchConfigType current_switch_config;
 };
 
 struct _MetaMonitorManagerClass
diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
index 8112f29..12eb16d 100644
--- a/src/backends/meta-monitor-manager.c
+++ b/src/backends/meta-monitor-manager.c
@@ -730,6 +730,7 @@ meta_monitor_manager_constructed (GObject *object)
                            G_CALLBACK (orientation_changed),
                            manager, 0);
 
+  manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
   manager->in_init = TRUE;
 
   /*
@@ -2883,6 +2884,8 @@ meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager)
 {
   MetaBackend *backend = meta_get_backend ();
 
+  manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
+
   meta_backend_monitors_changed (backend);
   g_signal_emit_by_name (manager, "monitors-changed");
 }
@@ -3258,3 +3261,53 @@ meta_monitor_manager_rotate_monitor (MetaMonitorManager *manager)
       g_object_unref (config);
     }
 }
+
+void
+meta_monitor_manager_switch_config (MetaMonitorManager          *manager,
+                                    MetaMonitorSwitchConfigType  config_type)
+{
+  g_return_if_fail (config_type != META_MONITOR_SWITCH_CONFIG_UNKNOWN);
+
+  if (!meta_is_monitor_config_manager_enabled ())
+    {
+      if (meta_monitor_config_switch_config (manager->legacy_config, config_type))
+        manager->current_switch_config = config_type;
+    }
+  else
+    {
+      GError *error = NULL;
+      MetaMonitorsConfig *config =
+        meta_monitor_config_manager_create_for_switch_config (manager->config_manager,
+                                                              config_type);
+      if (!config)
+        return;
+
+      if (!meta_monitor_manager_apply_monitors_config (manager,
+                                                       config,
+                                                       META_MONITORS_CONFIG_METHOD_TEMPORARY,
+                                                       &error))
+        {
+          g_warning ("Failed to use switch monitor configuration: %s",
+                     error->message);
+          g_error_free (error);
+        }
+      else
+        {
+          manager->current_switch_config = config_type;
+        }
+      g_object_unref (config);
+    }
+}
+
+gboolean
+meta_monitor_manager_can_switch_config (MetaMonitorManager *manager)
+{
+  return (!meta_monitor_manager_is_lid_closed (manager) &&
+          g_list_length (manager->monitors) > 1);
+}
+
+MetaMonitorSwitchConfigType
+meta_monitor_manager_get_switch_config (MetaMonitorManager *manager)
+{
+  return manager->current_switch_config;
+}
diff --git a/src/meta/meta-monitor-manager.h b/src/meta/meta-monitor-manager.h
index da1e329..22f7349 100644
--- a/src/meta/meta-monitor-manager.h
+++ b/src/meta/meta-monitor-manager.h
@@ -24,6 +24,15 @@
 
 #include <glib-object.h>
 
+typedef enum
+{
+  META_MONITOR_SWITCH_CONFIG_ALL_MIRROR,
+  META_MONITOR_SWITCH_CONFIG_ALL_LINEAR,
+  META_MONITOR_SWITCH_CONFIG_EXTERNAL,
+  META_MONITOR_SWITCH_CONFIG_BUILTIN,
+  META_MONITOR_SWITCH_CONFIG_UNKNOWN,
+} MetaMonitorSwitchConfigType;
+
 typedef struct _MetaMonitorManagerClass    MetaMonitorManagerClass;
 typedef struct _MetaMonitorManager         MetaMonitorManager;
 
@@ -39,4 +48,11 @@ gint meta_monitor_manager_get_monitor_for_connector (MetaMonitorManager *manager
 
 gboolean meta_monitor_manager_get_is_builtin_display_on (MetaMonitorManager *manager);
 
+void meta_monitor_manager_switch_config (MetaMonitorManager          *manager,
+                                         MetaMonitorSwitchConfigType  config_type);
+
+gboolean meta_monitor_manager_can_switch_config (MetaMonitorManager *manager);
+
+MetaMonitorSwitchConfigType meta_monitor_manager_get_switch_config (MetaMonitorManager *manager);
+
 #endif /* META_MONITOR_MANAGER_H */


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