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
This commit is contained in:
Rui Matos 2017-06-07 18:38:10 +02:00
parent 2bdd97e067
commit 3f9c5823cb
7 changed files with 474 additions and 3 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

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

View File

@ -354,6 +354,8 @@ struct _MetaMonitorManager
UpClient *up_client;
gulong experimental_features_changed_handler_id;
MetaMonitorSwitchConfigType current_switch_config;
};
struct _MetaMonitorManagerClass

View File

@ -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;
}

View File

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