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 */