From 9fa99a888d9d9b84cabf419d321f0223e0d5ab7e Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma 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 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 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 @@ -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 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