From 3f9c5823cbb976e4cc1c56f39cea7f81469c8349 Mon Sep 17 00:00:00 2001 From: Rui Matos Date: Wed, 7 Jun 2017 18:38:10 +0200 Subject: [PATCH] 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 91ee2ba72..6180b4e91 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 c79084ded..8f6f3dc64 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 b52785739..3ef3c626a 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 4199570d6..86450dc7e 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 9fed7cfb3..3ba74f31d 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 8112f290c..12eb16daf 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 da1e32922..22f734906 100644 --- a/src/meta/meta-monitor-manager.h +++ b/src/meta/meta-monitor-manager.h @@ -24,6 +24,15 @@ #include +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 */