diff --git a/src/Makefile.am b/src/Makefile.am
index d39b73b26..5c0a55383 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,6 +111,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES = \
backends/meta-logical-monitor.h \
backends/meta-monitor-config-manager.c \
backends/meta-monitor-config-manager.h \
+ backends/meta-monitor-config-migration.c \
+ backends/meta-monitor-config-migration.h \
backends/meta-monitor-config-store.c \
backends/meta-monitor-config-store.h \
backends/meta-monitor.c \
diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c
index 90514b6a9..d84f7c509 100644
--- a/src/backends/meta-monitor-config-manager.c
+++ b/src/backends/meta-monitor-config-manager.c
@@ -23,6 +23,7 @@
#include "backends/meta-monitor-config-manager.h"
+#include "backends/meta-monitor-config-migration.h"
#include "backends/meta-monitor-config-store.h"
#include "backends/meta-monitor-manager-private.h"
#include "core/boxes-private.h"
@@ -361,10 +362,12 @@ create_key_for_current_state (MetaMonitorManager *monitor_manager)
MetaMonitorsConfig *
meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager)
{
+ MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
MetaMonitorsConfigKey *config_key;
MetaMonitorsConfig *config;
+ GError *error = NULL;
- config_key = create_key_for_current_state (config_manager->monitor_manager);
+ config_key = create_key_for_current_state (monitor_manager);
if (!config_key)
return NULL;
@@ -372,6 +375,22 @@ meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager
config_key);
meta_monitors_config_key_free (config_key);
+ if (!config)
+ return NULL;
+
+ if (config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED)
+ {
+ if (!meta_finish_monitors_config_migration (monitor_manager, config,
+ &error))
+ {
+ g_warning ("Failed to finish monitors config migration: %s",
+ error->message);
+ g_error_free (error);
+ meta_monitor_config_store_remove (config_manager->config_store, config);
+ return NULL;
+ }
+ }
+
return config;
}
@@ -591,7 +610,8 @@ meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_mana
x += logical_monitor_config->layout.width;
}
- return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ return meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
MetaMonitorsConfig *
@@ -619,7 +639,8 @@ meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_ma
logical_monitor_configs = g_list_append (NULL,
primary_logical_monitor_config);
- return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ return meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
MetaMonitorsConfig *
@@ -694,7 +715,8 @@ meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_m
if (!logical_monitor_configs)
return NULL;
- return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ return meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
static MetaMonitorsConfig *
@@ -741,7 +763,8 @@ create_for_builtin_display_rotation (MetaMonitorConfigManager *config_manager,
logical_monitor_config->transform = transform;
return meta_monitors_config_new (g_list_append (NULL, logical_monitor_config),
- config_manager->current_config->layout_mode);
+ config_manager->current_config->layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
MetaMonitorsConfig *
@@ -761,6 +784,7 @@ static MetaMonitorsConfig *
create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager)
{
MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
+ MetaLogicalMonitorLayoutMode layout_mode;
MetaLogicalMonitorConfig *logical_monitor_config = NULL;
GList *monitor_configs = NULL;
gint common_mode_w = 0, common_mode_h = 0;
@@ -859,8 +883,10 @@ create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager)
.monitor_configs = monitor_configs
};
+ layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
return meta_monitors_config_new (g_list_append (NULL, logical_monitor_config),
- meta_monitor_manager_get_default_layout_mode (monitor_manager));
+ layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
static MetaMonitorsConfig *
@@ -899,7 +925,8 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager)
x += logical_monitor_config->layout.width;
}
- return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ return meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
static MetaMonitorsConfig *
@@ -927,7 +954,8 @@ create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager)
logical_monitor_configs = g_list_append (NULL,
primary_logical_monitor_config);
- return meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ return meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
}
MetaMonitorsConfig *
@@ -1117,7 +1145,8 @@ meta_monitors_config_key_equal (gconstpointer data_a,
MetaMonitorsConfig *
meta_monitors_config_new (GList *logical_monitor_configs,
- MetaLogicalMonitorLayoutMode layout_mode)
+ MetaLogicalMonitorLayoutMode layout_mode,
+ MetaMonitorsConfigFlag flags)
{
MetaMonitorsConfig *config;
@@ -1125,6 +1154,7 @@ meta_monitors_config_new (GList *logical_monitor_configs,
config->logical_monitor_configs = logical_monitor_configs;
config->key = meta_monitors_config_key_new (logical_monitor_configs);
config->layout_mode = layout_mode;
+ config->flags = flags;
return config;
}
diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h
index 8f6f3dc64..aa2646ebd 100644
--- a/src/backends/meta-monitor-config-manager.h
+++ b/src/backends/meta-monitor-config-manager.h
@@ -51,6 +51,12 @@ typedef struct _MetaMonitorsConfigKey
GList *monitor_specs;
} MetaMonitorsConfigKey;
+typedef enum _MetaMonitorsConfigFlag
+{
+ META_MONITORS_CONFIG_FLAG_NONE = 0,
+ META_MONITORS_CONFIG_FLAG_MIGRATED = (1 << 0),
+} MetaMonitorsConfigFlag;
+
struct _MetaMonitorsConfig
{
GObject parent;
@@ -58,6 +64,8 @@ struct _MetaMonitorsConfig
MetaMonitorsConfigKey *key;
GList *logical_monitor_configs;
+ MetaMonitorsConfigFlag flags;
+
MetaLogicalMonitorLayoutMode layout_mode;
};
@@ -101,7 +109,8 @@ MetaMonitorsConfig * meta_monitor_config_manager_get_previous (MetaMonitorConfig
void meta_monitor_config_manager_save_current (MetaMonitorConfigManager *config_manager);
MetaMonitorsConfig * meta_monitors_config_new (GList *logical_monitor_configs,
- MetaLogicalMonitorLayoutMode layout_mode);
+ MetaLogicalMonitorLayoutMode layout_mode,
+ MetaMonitorsConfigFlag flags);
unsigned int meta_monitors_config_key_hash (gconstpointer config_key);
diff --git a/src/backends/meta-monitor-config-migration.c b/src/backends/meta-monitor-config-migration.c
new file mode 100644
index 000000000..b3f211103
--- /dev/null
+++ b/src/backends/meta-monitor-config-migration.c
@@ -0,0 +1,1193 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2001, 2002 Havoc Pennington
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ * Some ICCCM manager selection code derived from fvwm2,
+ * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2004-2006 Elijah Newren
+ * Copyright (C) 2013, 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+/*
+ * Portions of this file are derived from gnome-desktop/libgnome-desktop/gnome-rr-config.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * Copyright 2010 Giovanni Campagna
+ *
+ * Author: Soren Sandmann
+ */
+
+#include "config.h"
+
+#include "backends/meta-monitor-config-migration.h"
+
+#include
+#include
+
+#include "backends/meta-monitor-config-manager.h"
+#include "backends/meta-monitor-config-store.h"
+#include "backends/meta-monitor-manager-private.h"
+#include "meta/boxes.h"
+
+#define META_MONITORS_CONFIG_MIGRATION_ERROR (meta_monitors_config_migration_error_quark ())
+static GQuark meta_monitors_config_migration_error_quark (void);
+
+G_DEFINE_QUARK (meta-monitors-config-migration-error-quark,
+ meta_monitors_config_migration_error)
+
+enum _MetaConfigMigrationError
+{
+ META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
+ META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE
+} MetaConfigMigrationError;
+
+typedef struct
+{
+ char *connector;
+ char *vendor;
+ char *product;
+ char *serial;
+} MetaOutputKey;
+
+typedef struct
+{
+ gboolean enabled;
+ MetaRectangle rect;
+ float refresh_rate;
+ MetaMonitorTransform transform;
+
+ gboolean is_primary;
+ gboolean is_presentation;
+ gboolean is_underscanning;
+} MetaOutputConfig;
+
+typedef struct _MetaLegacyMonitorsConfig
+{
+ MetaOutputKey *keys;
+ MetaOutputConfig *outputs;
+ unsigned int n_outputs;
+} MetaLegacyMonitorsConfig;
+
+typedef enum
+{
+ STATE_INITIAL,
+ STATE_MONITORS,
+ STATE_CONFIGURATION,
+ STATE_OUTPUT,
+ STATE_OUTPUT_FIELD,
+ STATE_CLONE
+} ParserState;
+
+typedef struct
+{
+ ParserState state;
+ int unknown_count;
+
+ GArray *key_array;
+ GArray *output_array;
+ MetaOutputKey key;
+ MetaOutputConfig output;
+
+ char *output_field;
+
+ GHashTable *configs;
+} ConfigParser;
+
+static MetaLegacyMonitorsConfig *
+legacy_config_new (void)
+{
+ return g_new0 (MetaLegacyMonitorsConfig, 1);
+}
+
+static void
+legacy_config_free (gpointer data)
+{
+ MetaLegacyMonitorsConfig *config = data;
+
+ g_free (config->keys);
+ g_free (config->outputs);
+ g_free (config);
+}
+
+static unsigned long
+output_key_hash (const MetaOutputKey *key)
+{
+ return (g_str_hash (key->connector) ^
+ g_str_hash (key->vendor) ^
+ g_str_hash (key->product) ^
+ g_str_hash (key->serial));
+}
+
+static gboolean
+output_key_equal (const MetaOutputKey *one,
+ const MetaOutputKey *two)
+{
+ return (strcmp (one->connector, two->connector) == 0 &&
+ strcmp (one->vendor, two->vendor) == 0 &&
+ strcmp (one->product, two->product) == 0 &&
+ strcmp (one->serial, two->serial) == 0);
+}
+
+static unsigned int
+legacy_config_hash (gconstpointer data)
+{
+ const MetaLegacyMonitorsConfig *config = data;
+ unsigned int i, hash;
+
+ hash = 0;
+ for (i = 0; i < config->n_outputs; i++)
+ hash ^= output_key_hash (&config->keys[i]);
+
+ return hash;
+}
+
+static gboolean
+legacy_config_equal (gconstpointer one,
+ gconstpointer two)
+{
+ const MetaLegacyMonitorsConfig *c_one = one;
+ const MetaLegacyMonitorsConfig *c_two = two;
+ unsigned int i;
+ gboolean ok;
+
+ if (c_one->n_outputs != c_two->n_outputs)
+ return FALSE;
+
+ ok = TRUE;
+ for (i = 0; i < c_one->n_outputs && ok; i++)
+ ok = output_key_equal (&c_one->keys[i],
+ &c_two->keys[i]);
+
+ return ok;
+}
+
+static void
+free_output_key (MetaOutputKey *key)
+{
+ g_free (key->connector);
+ g_free (key->vendor);
+ g_free (key->product);
+ g_free (key->serial);
+}
+
+static void
+handle_start_element (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_INITIAL:
+ {
+ char *version;
+
+ if (strcmp (element_name, "monitors") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Invalid document element %s", element_name);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "version", &version,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (strcmp (version, "1") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid or unsupported version %s", version);
+ return;
+ }
+
+ parser->state = STATE_MONITORS;
+ return;
+ }
+
+ case STATE_MONITORS:
+ {
+ if (strcmp (element_name, "configuration") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Invalid toplevel element %s", element_name);
+ return;
+ }
+
+ parser->key_array = g_array_new (FALSE, FALSE,
+ sizeof (MetaOutputKey));
+ parser->output_array = g_array_new (FALSE, FALSE,
+ sizeof (MetaOutputConfig));
+ parser->state = STATE_CONFIGURATION;
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (strcmp (element_name, "clone") == 0 &&
+ parser->unknown_count == 0)
+ {
+ parser->state = STATE_CLONE;
+ }
+ else if (strcmp (element_name, "output") == 0 &&
+ parser->unknown_count == 0)
+ {
+ char *name;
+
+ if (!g_markup_collect_attributes (element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "name", &name,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ memset (&parser->key, 0, sizeof (MetaOutputKey));
+ memset (&parser->output, 0, sizeof (MetaOutputConfig));
+
+ parser->key.connector = g_strdup (name);
+ parser->state = STATE_OUTPUT;
+ }
+ else
+ {
+ parser->unknown_count++;
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if ((strcmp (element_name, "vendor") == 0 ||
+ strcmp (element_name, "product") == 0 ||
+ strcmp (element_name, "serial") == 0 ||
+ strcmp (element_name, "width") == 0 ||
+ strcmp (element_name, "height") == 0 ||
+ strcmp (element_name, "rate") == 0 ||
+ strcmp (element_name, "x") == 0 ||
+ strcmp (element_name, "y") == 0 ||
+ strcmp (element_name, "rotation") == 0 ||
+ strcmp (element_name, "reflect_x") == 0 ||
+ strcmp (element_name, "reflect_y") == 0 ||
+ strcmp (element_name, "primary") == 0 ||
+ strcmp (element_name, "presentation") == 0 ||
+ strcmp (element_name, "underscanning") == 0) &&
+ parser->unknown_count == 0)
+ {
+ parser->state = STATE_OUTPUT_FIELD;
+
+ parser->output_field = g_strdup (element_name);
+ }
+ else
+ {
+ parser->unknown_count++;
+ }
+
+ return;
+ }
+
+ case STATE_CLONE:
+ case STATE_OUTPUT_FIELD:
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected element %s", element_name);
+ return;
+ }
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+handle_end_element (GMarkupParseContext *context,
+ const char *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_MONITORS:
+ {
+ parser->state = STATE_INITIAL;
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (strcmp (element_name, "configuration") == 0 &&
+ parser->unknown_count == 0)
+ {
+ MetaLegacyMonitorsConfig *config = legacy_config_new ();
+
+ g_assert (parser->key_array->len == parser->output_array->len);
+
+ config->n_outputs = parser->key_array->len;
+ config->keys = (void*)g_array_free (parser->key_array, FALSE);
+ config->outputs = (void*)g_array_free (parser->output_array, FALSE);
+
+ g_hash_table_replace (parser->configs, config, config);
+
+ parser->key_array = NULL;
+ parser->output_array = NULL;
+ parser->state = STATE_MONITORS;
+ }
+ else
+ {
+ parser->unknown_count--;
+
+ g_assert (parser->unknown_count >= 0);
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0)
+ {
+ if (parser->key.vendor == NULL ||
+ parser->key.product == NULL ||
+ parser->key.serial == NULL)
+ {
+ /* Disconnected output, ignore */
+ free_output_key (&parser->key);
+ }
+ else
+ {
+ if (parser->output.rect.width == 0 ||
+ parser->output.rect.height == 0)
+ parser->output.enabled = FALSE;
+ else
+ parser->output.enabled = TRUE;
+
+ g_array_append_val (parser->key_array, parser->key);
+ g_array_append_val (parser->output_array, parser->output);
+ }
+
+ memset (&parser->key, 0, sizeof (MetaOutputKey));
+ memset (&parser->output, 0, sizeof (MetaOutputConfig));
+
+ parser->state = STATE_CONFIGURATION;
+ }
+ else
+ {
+ parser->unknown_count--;
+
+ g_assert (parser->unknown_count >= 0);
+ }
+
+ return;
+ }
+
+ case STATE_CLONE:
+ {
+ parser->state = STATE_CONFIGURATION;
+ return;
+ }
+
+ case STATE_OUTPUT_FIELD:
+ {
+ g_free (parser->output_field);
+ parser->output_field = NULL;
+
+ parser->state = STATE_OUTPUT;
+ return;
+ }
+
+ case STATE_INITIAL:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+read_int (const char *text,
+ gsize text_len,
+ gint *field,
+ GError **error)
+{
+ char buf[64];
+ gint64 v;
+ char *end;
+
+ strncpy (buf, text, text_len);
+ buf[MIN (63, text_len)] = 0;
+
+ v = g_ascii_strtoll (buf, &end, 10);
+
+ /* Limit reasonable values (actual limits are a lot smaller that these) */
+ if (*end || v < 0 || v > G_MAXINT16)
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Expected a number, got %s", buf);
+ else
+ *field = v;
+}
+
+static void
+read_float (const char *text,
+ gsize text_len,
+ gfloat *field,
+ GError **error)
+{
+ char buf[64];
+ gfloat v;
+ char *end;
+
+ strncpy (buf, text, text_len);
+ buf[MIN (63, text_len)] = 0;
+
+ v = g_ascii_strtod (buf, &end);
+
+ /* Limit reasonable values (actual limits are a lot smaller that these) */
+ if (*end)
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Expected a number, got %s", buf);
+ else
+ *field = v;
+}
+
+static gboolean
+read_bool (const char *text,
+ gsize text_len,
+ GError **error)
+{
+ if (strncmp (text, "no", text_len) == 0)
+ return FALSE;
+ else if (strncmp (text, "yes", text_len) == 0)
+ return TRUE;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid boolean value %.*s", (int)text_len, text);
+
+ return FALSE;
+}
+
+static gboolean
+is_all_whitespace (const char *text,
+ gsize text_len)
+{
+ gsize i;
+
+ for (i = 0; i < text_len; i++)
+ if (!g_ascii_isspace (text[i]))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+handle_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_MONITORS:
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (parser->unknown_count == 0)
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ }
+ else
+ {
+ /* Handling unknown element, ignore */
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if (parser->unknown_count == 0)
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ }
+ else
+ {
+ /* Handling unknown element, ignore */
+ }
+ return;
+ }
+
+ case STATE_CLONE:
+ {
+ /* Ignore the clone flag */
+ return;
+ }
+
+ case STATE_OUTPUT_FIELD:
+ {
+ if (strcmp (parser->output_field, "vendor") == 0)
+ parser->key.vendor = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "product") == 0)
+ parser->key.product = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "serial") == 0)
+ parser->key.serial = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "width") == 0)
+ read_int (text, text_len, &parser->output.rect.width, error);
+ else if (strcmp (parser->output_field, "height") == 0)
+ read_int (text, text_len, &parser->output.rect.height, error);
+ else if (strcmp (parser->output_field, "rate") == 0)
+ read_float (text, text_len, &parser->output.refresh_rate, error);
+ else if (strcmp (parser->output_field, "x") == 0)
+ read_int (text, text_len, &parser->output.rect.x, error);
+ else if (strcmp (parser->output_field, "y") == 0)
+ read_int (text, text_len, &parser->output.rect.y, error);
+ else if (strcmp (parser->output_field, "rotation") == 0)
+ {
+ if (strncmp (text, "normal", text_len) == 0)
+ parser->output.transform = META_MONITOR_TRANSFORM_NORMAL;
+ else if (strncmp (text, "left", text_len) == 0)
+ parser->output.transform = META_MONITOR_TRANSFORM_90;
+ else if (strncmp (text, "upside_down", text_len) == 0)
+ parser->output.transform = META_MONITOR_TRANSFORM_180;
+ else if (strncmp (text, "right", text_len) == 0)
+ parser->output.transform = META_MONITOR_TRANSFORM_270;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid rotation type %.*s", (int)text_len, text);
+ }
+ else if (strcmp (parser->output_field, "reflect_x") == 0)
+ parser->output.transform += read_bool (text, text_len, error) ?
+ META_MONITOR_TRANSFORM_FLIPPED : 0;
+ else if (strcmp (parser->output_field, "reflect_y") == 0)
+ {
+ if (read_bool (text, text_len, error))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Y reflection is not supported");
+ }
+ else if (strcmp (parser->output_field, "primary") == 0)
+ parser->output.is_primary = read_bool (text, text_len, error);
+ else if (strcmp (parser->output_field, "presentation") == 0)
+ parser->output.is_presentation = read_bool (text, text_len, error);
+ else if (strcmp (parser->output_field, "underscanning") == 0)
+ parser->output.is_underscanning = read_bool (text, text_len, error);
+ else
+ g_assert_not_reached ();
+ return;
+ }
+
+ case STATE_INITIAL:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static const GMarkupParser config_parser = {
+ .start_element = handle_start_element,
+ .end_element = handle_end_element,
+ .text = handle_text,
+};
+
+static GHashTable *
+load_config_file (GFile *file,
+ GError **error)
+{
+ g_autofree char *contents = NULL;
+ gsize size;
+ g_autoptr (GMarkupParseContext) context = NULL;
+ ConfigParser parser = { 0 };
+
+ if (!g_file_load_contents (file, NULL, &contents, &size, NULL, error))
+ return FALSE;
+
+ parser.configs = g_hash_table_new_full (legacy_config_hash,
+ legacy_config_equal,
+ legacy_config_free,
+ NULL);
+ parser.state = STATE_INITIAL;
+
+ context = g_markup_parse_context_new (&config_parser,
+ G_MARKUP_TREAT_CDATA_AS_TEXT |
+ G_MARKUP_PREFIX_ERROR_POSITION,
+ &parser, NULL);
+ if (!g_markup_parse_context_parse (context, contents, size, error))
+ {
+ if (parser.key_array)
+ g_array_free (parser.key_array, TRUE);
+ if (parser.output_array)
+ g_array_free (parser.output_array, TRUE);
+
+ free_output_key (&parser.key);
+ g_hash_table_destroy (parser.configs);
+
+ return NULL;
+ }
+
+ return parser.configs;
+}
+
+static MetaMonitorConfig *
+create_monitor_config (MetaOutputKey *output_key,
+ MetaOutputConfig *output_config,
+ int mode_width,
+ int mode_height,
+ GError **error)
+{
+ MetaMonitorModeSpec *mode_spec;
+ MetaMonitorSpec *monitor_spec;
+ MetaMonitorConfig *monitor_config;
+
+ mode_spec = g_new0 (MetaMonitorModeSpec, 1);
+ *mode_spec = (MetaMonitorModeSpec) {
+ .width = mode_width,
+ .height = mode_height,
+ .refresh_rate = output_config->refresh_rate
+ };
+
+ if (!meta_verify_monitor_mode_spec (mode_spec, error))
+ {
+ g_free (mode_spec);
+ return NULL;
+ }
+
+ monitor_spec = g_new0 (MetaMonitorSpec, 1);
+ *monitor_spec = (MetaMonitorSpec) {
+ .connector = output_key->connector,
+ .vendor = output_key->vendor,
+ .product = output_key->product,
+ .serial = output_key->serial
+ };
+
+ monitor_config = g_new0 (MetaMonitorConfig, 1);
+ *monitor_config = (MetaMonitorConfig) {
+ .monitor_spec = monitor_spec,
+ .mode_spec = mode_spec,
+ .enable_underscanning = output_config->is_underscanning
+ };
+
+ if (!meta_verify_monitor_config (monitor_config, error))
+ {
+ meta_monitor_config_free (monitor_config);
+ return NULL;
+ }
+
+ return monitor_config;
+}
+
+typedef struct _MonitorTile
+{
+ MetaOutputKey *output_key;
+ MetaOutputConfig *output_config;
+} MonitorTile;
+
+static MetaMonitorConfig *
+try_derive_tiled_monitor_config (MetaLegacyMonitorsConfig *config,
+ MetaOutputKey *output_key,
+ MetaOutputConfig *output_config,
+ MetaMonitorConfigStore *config_store,
+ MetaRectangle *out_layout,
+ GError **error)
+{
+ MonitorTile top_left_tile = { 0 };
+ MonitorTile top_right_tile = { 0 };
+ MonitorTile bottom_left_tile = { 0 };
+ MonitorTile bottom_right_tile = { 0 };
+ MonitorTile origin_tile = { 0 };
+ MetaMonitorTransform transform = output_config->transform;
+ unsigned int i;
+ int max_x = 0;
+ int min_x = INT_MAX;
+ int max_y = 0;
+ int min_y = INT_MAX;
+ int mode_width = 0;
+ int mode_height = 0;
+ MetaMonitorConfig *monitor_config;
+
+ /*
+ * In order to derive a monitor configuration for a tiled monitor,
+ * try to find the origin tile, then combine the discovered output
+ * tiles to given the configured transform a monitor mode.
+ *
+ * If the origin tile is not the main tile (tile always enabled
+ * even for non-tiled modes), this will fail, but since infermation
+ * about tiling is lost, there is no way to discover it.
+ */
+
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ MetaOutputKey *other_output_key = &config->keys[i];
+ MetaOutputConfig *other_output_config = &config->outputs[i];
+ MetaRectangle *rect;
+
+ if (strcmp (output_key->vendor, other_output_key->vendor) != 0 ||
+ strcmp (output_key->product, other_output_key->product) != 0 ||
+ strcmp (output_key->serial, other_output_key->serial) != 0)
+ continue;
+
+ rect = &other_output_config->rect;
+ min_x = MIN (min_x, rect->x);
+ min_y = MIN (min_y, rect->y);
+ max_x = MAX (max_x, rect->x + rect->width);
+ max_y = MAX (max_y, rect->y + rect->height);
+
+ if (min_x == rect->x &&
+ min_y == rect->y)
+ {
+ top_left_tile = (MonitorTile) {
+ .output_key = other_output_key,
+ .output_config = other_output_config
+ };
+ }
+ if (max_x == rect->x + rect->width &&
+ min_y == rect->y)
+ {
+ top_right_tile = (MonitorTile) {
+ .output_key = other_output_key,
+ .output_config = other_output_config
+ };
+ }
+ if (min_x == rect->x &&
+ max_y == rect->y + rect->height)
+ {
+ bottom_left_tile = (MonitorTile) {
+ .output_key = other_output_key,
+ .output_config = other_output_config
+ };
+ }
+ if (max_x == rect->x + rect->width &&
+ max_y == rect->y + rect->height)
+ {
+ bottom_right_tile = (MonitorTile) {
+ .output_key = other_output_key,
+ .output_config = other_output_config
+ };
+ }
+ }
+
+ if (top_left_tile.output_key == bottom_right_tile.output_key)
+ {
+ g_set_error_literal (error,
+ META_MONITORS_CONFIG_MIGRATION_ERROR,
+ META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
+ "Not a tiled monitor");
+ return NULL;
+ }
+
+ switch (transform)
+ {
+ case META_MONITOR_TRANSFORM_NORMAL:
+ origin_tile = top_left_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+ case META_MONITOR_TRANSFORM_90:
+ origin_tile = bottom_left_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+ case META_MONITOR_TRANSFORM_180:
+ origin_tile = bottom_right_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+ case META_MONITOR_TRANSFORM_270:
+ origin_tile = top_right_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+ case META_MONITOR_TRANSFORM_FLIPPED:
+ origin_tile = bottom_left_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+ case META_MONITOR_TRANSFORM_FLIPPED_90:
+ origin_tile = bottom_right_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+ case META_MONITOR_TRANSFORM_FLIPPED_180:
+ origin_tile = top_right_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+ case META_MONITOR_TRANSFORM_FLIPPED_270:
+ origin_tile = top_left_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+ }
+
+ g_assert (origin_tile.output_key);
+ g_assert (origin_tile.output_config);
+
+ if (origin_tile.output_key != output_key)
+ {
+ g_set_error_literal (error,
+ META_MONITORS_CONFIG_MIGRATION_ERROR,
+ META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE,
+ "Not the main tile");
+ return NULL;
+ }
+
+ monitor_config = create_monitor_config (origin_tile.output_key,
+ origin_tile.output_config,
+ mode_width, mode_height,
+ error);
+ if (!monitor_config)
+ return NULL;
+
+ *out_layout = (MetaRectangle) {
+ .x = min_x,
+ .y = min_y,
+ .width = max_x - min_x,
+ .height = max_y - min_y
+ };
+
+ return monitor_config;
+}
+
+static MetaMonitorConfig *
+derive_monitor_config (MetaOutputKey *output_key,
+ MetaOutputConfig *output_config,
+ MetaRectangle *out_layout,
+ GError **error)
+{
+ int mode_width;
+ int mode_height;
+ MetaMonitorConfig *monitor_config;
+
+ if (meta_monitor_transform_is_rotated (output_config->transform))
+ {
+ mode_width = output_config->rect.height;
+ mode_height = output_config->rect.width;
+ }
+ else
+ {
+ mode_width = output_config->rect.width;
+ mode_height = output_config->rect.height;
+ }
+
+ monitor_config = create_monitor_config (output_key, output_config,
+ mode_width, mode_height,
+ error);
+ if (!monitor_config)
+ return NULL;
+
+ *out_layout = output_config->rect;
+
+ return monitor_config;
+}
+
+static MetaLogicalMonitorConfig *
+ensure_logical_monitor (GList **logical_monitor_configs,
+ MetaOutputConfig *output_config,
+ MetaRectangle *layout)
+{
+ MetaLogicalMonitorConfig *new_logical_monitor_config;
+ GList *l;
+
+ for (l = *logical_monitor_configs; l; l = l->next)
+ {
+ MetaLogicalMonitorConfig *logical_monitor_config = l->data;
+
+ if (meta_rectangle_equal (&logical_monitor_config->layout, layout))
+ return logical_monitor_config;
+ }
+
+ new_logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
+ *new_logical_monitor_config = (MetaLogicalMonitorConfig) {
+ .layout = *layout,
+ .is_primary = output_config->is_primary,
+ .is_presentation = output_config->is_presentation,
+ .transform = output_config->transform,
+ .scale = -1.0,
+ };
+
+ *logical_monitor_configs = g_list_append (*logical_monitor_configs,
+ new_logical_monitor_config);
+
+ return new_logical_monitor_config;
+}
+
+static GList *
+derive_logical_monitor_configs (MetaLegacyMonitorsConfig *config,
+ MetaMonitorConfigStore *config_store,
+ GError **error)
+{
+ GList *logical_monitor_configs = NULL;
+ GList *monitor_configs = NULL;
+ unsigned int i;
+
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ MetaOutputKey *output_key = &config->keys[i];
+ MetaOutputConfig *output_config = &config->outputs[i];
+ MetaMonitorConfig *monitor_config = NULL;
+ MetaRectangle layout;
+ MetaLogicalMonitorConfig *logical_monitor_config;
+
+ if (!output_config->enabled)
+ continue;
+
+ if (output_key->vendor &&
+ g_strcmp0 (output_key->vendor, "unknown") != 0 &&
+ output_key->product &&
+ g_strcmp0 (output_key->product, "unknown") != 0 &&
+ output_key->serial &&
+ g_strcmp0 (output_key->serial, "unknown") != 0)
+ {
+ monitor_config = try_derive_tiled_monitor_config (config,
+ output_key,
+ output_config,
+ config_store,
+ &layout,
+ error);
+ if (!monitor_config)
+ {
+ if ((*error)->domain == META_MONITORS_CONFIG_MIGRATION_ERROR)
+ {
+ int error_code = (*error)->code;
+
+ g_clear_error (error);
+
+ switch (error_code)
+ {
+ case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED:
+ break;
+ case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE:
+ continue;
+ }
+ }
+ else
+ {
+ g_list_free_full (monitor_configs,
+ (GDestroyNotify) meta_monitor_config_free);
+ return NULL;
+ }
+ }
+ }
+
+ if (!monitor_config)
+ monitor_config = derive_monitor_config (output_key, output_config,
+ &layout,
+ error);
+
+ if (!monitor_config)
+ {
+ g_list_free_full (monitor_configs,
+ (GDestroyNotify) meta_monitor_config_free);
+ return NULL;
+ }
+
+ logical_monitor_config =
+ ensure_logical_monitor (&logical_monitor_configs,
+ output_config, &layout);
+
+ logical_monitor_config->monitor_configs =
+ g_list_append (logical_monitor_config->monitor_configs, monitor_config);
+ }
+
+ if (!logical_monitor_configs)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Empty configuration");
+ return NULL;
+ }
+
+ return logical_monitor_configs;
+}
+
+static char *
+generate_config_name (MetaLegacyMonitorsConfig *config)
+{
+ char **output_strings;
+ unsigned int i;
+ char *key_name;
+
+ output_strings = g_new0 (char *, config->n_outputs + 1);
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ MetaOutputKey *output_key = &config->keys[i];
+
+ output_strings[i] = g_strdup_printf ("%s:%s:%s:%s",
+ output_key->connector,
+ output_key->vendor,
+ output_key->product,
+ output_key->serial);
+ }
+
+ key_name = g_strjoinv (", ", output_strings);
+
+ g_strfreev (output_strings);
+
+ return key_name;
+}
+
+static void
+migrate_config (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ MetaLegacyMonitorsConfig *legacy_config = key;
+ MetaMonitorConfigStore *config_store = user_data;
+ MetaMonitorManager *monitor_manager =
+ meta_monitor_config_store_get_monitor_manager (config_store);
+ GList *logical_monitor_configs;
+ MetaLogicalMonitorLayoutMode layout_mode;
+ GError *error = NULL;
+ MetaMonitorsConfig *config;
+
+ logical_monitor_configs = derive_logical_monitor_configs (legacy_config,
+ config_store,
+ &error);
+ if (!logical_monitor_configs)
+ {
+ g_autofree char *config_name = NULL;
+
+ config_name = generate_config_name (legacy_config);
+ g_warning ("Failed to migrate monitor configuration for %s: %s",
+ config_name, error->message);
+ return;
+ }
+
+ layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
+ config = meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_MIGRATED);
+ if (!meta_verify_monitors_config (config, monitor_manager, &error))
+ {
+ g_autofree char *config_name = NULL;
+
+ config_name = generate_config_name (legacy_config);
+ g_warning ("Ignoring invalid monitor configuration for %s: %s",
+ config_name, error->message);
+ g_object_unref (config);
+ return;
+ }
+
+ meta_monitor_config_store_add (config_store, config);
+}
+
+gboolean
+meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store,
+ GFile *in_file,
+ GError **error)
+{
+ g_autoptr (GHashTable) configs = NULL;
+
+ configs = load_config_file (in_file, error);
+ if (!configs)
+ return FALSE;
+
+ g_hash_table_foreach (configs, migrate_config, config_store);
+ g_hash_table_destroy (configs);
+
+ return TRUE;
+}
+
+gboolean
+meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store,
+ GError **error)
+{
+ g_autofree char *backup_path = NULL;
+ g_autoptr (GFile) backup_file = NULL;
+ g_autofree char *user_file_path = NULL;
+ g_autoptr (GFile) user_file = NULL;
+
+ user_file_path = g_build_filename (g_get_user_config_dir (),
+ "monitors.xml",
+ NULL);
+ user_file = g_file_new_for_path (user_file_path);
+ backup_path = g_build_filename (g_get_user_config_dir (),
+ "monitors-v1-backup.xml",
+ NULL);
+ backup_file = g_file_new_for_path (backup_path);
+
+ if (!g_file_copy (user_file, backup_file,
+ G_FILE_COPY_OVERWRITE | G_FILE_COPY_BACKUP,
+ NULL, NULL, NULL,
+ error))
+ {
+ g_warning ("Failed to make a backup of monitors.xml: %s",
+ (*error)->message);
+ g_clear_error (error);
+ }
+
+ return meta_migrate_old_monitors_config (config_store, user_file, error);
+}
+
+gboolean
+meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager,
+ MetaMonitorsConfig *config,
+ GError **error)
+{
+ MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
+ MetaMonitorConfigStore *config_store =
+ meta_monitor_config_manager_get_store (config_manager);
+ GList *l;
+
+ for (l = config->logical_monitor_configs; l; l = l->next)
+ {
+ MetaLogicalMonitorConfig *logical_monitor_config = l->data;
+ MetaMonitorConfig *monitor_config;
+ MetaMonitorSpec *monitor_spec;
+ MetaMonitor *monitor;
+ MetaMonitorModeSpec *monitor_mode_spec;
+ MetaMonitorMode *monitor_mode;
+ float scale;
+
+ monitor_config = logical_monitor_config->monitor_configs->data;
+ monitor_spec = monitor_config->monitor_spec;
+ monitor = meta_monitor_manager_get_monitor_from_spec (monitor_manager,
+ monitor_spec);
+ monitor_mode_spec = monitor_config->mode_spec;
+ monitor_mode = meta_monitor_get_mode_from_spec (monitor,
+ monitor_mode_spec);
+ scale = meta_monitor_calculate_mode_scale (monitor, monitor_mode);
+
+ logical_monitor_config->scale = scale;
+ }
+
+ config->layout_mode =
+ meta_monitor_manager_get_default_layout_mode (monitor_manager);
+ config->flags &= ~META_MONITORS_CONFIG_FLAG_MIGRATED;
+
+ if (!meta_verify_monitors_config (config, monitor_manager, error))
+ return FALSE;
+
+ meta_monitor_config_store_add (config_store, config);
+
+ return TRUE;
+}
diff --git a/src/backends/meta-monitor-config-migration.h b/src/backends/meta-monitor-config-migration.h
new file mode 100644
index 000000000..4ea21cb1a
--- /dev/null
+++ b/src/backends/meta-monitor-config-migration.h
@@ -0,0 +1,38 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_MONITOR_CONFIG_MIGRATION_H
+#define META_MONITOR_CONFIG_MIGRATION_H
+
+#include "backends/meta-monitor-manager-private.h"
+
+gboolean meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store,
+ GFile *in_file,
+ GError **error);
+
+gboolean meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store,
+ GError **error);
+
+gboolean meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager,
+ MetaMonitorsConfig *config,
+ GError **error);
+
+#endif /* META_MONITOR_CONFIG_MIGRATION_H */
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
index d70cddab8..be9de65c3 100644
--- a/src/backends/meta-monitor-config-store.c
+++ b/src/backends/meta-monitor-config-store.c
@@ -27,6 +27,7 @@
#include
#include "backends/meta-monitor-config-manager.h"
+#include "backends/meta-monitor-config-migration.h"
#define MONITORS_CONFIG_XML_FORMAT_VERSION 2
@@ -109,14 +110,27 @@ struct _MetaMonitorConfigStore
GCancellable *save_cancellable;
GFile *user_file;
- GFile *custom_file;
+ GFile *custom_read_file;
+ GFile *custom_write_file;
};
+#define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ())
+static GQuark meta_monitor_config_store_error_quark (void);
+
+enum
+{
+ META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION
+};
+
+G_DEFINE_QUARK (meta-monitor-config-store-error-quark,
+ meta_monitor_config_store_error)
+
typedef enum
{
STATE_INITIAL,
STATE_MONITORS,
STATE_CONFIGURATION,
+ STATE_MIGRATED,
STATE_LOGICAL_MONITOR,
STATE_LOGICAL_MONITOR_X,
STATE_LOGICAL_MONITOR_Y,
@@ -145,6 +159,7 @@ typedef struct
ParserState state;
MetaMonitorConfigStore *config_store;
+ gboolean current_was_migrated;
GList *current_logical_monitor_configs;
MetaMonitorSpec *current_monitor_spec;
gboolean current_transform_flipped;
@@ -189,7 +204,14 @@ handle_start_element (GMarkupParseContext *context,
"Missing config file format version");
}
- /* TODO: Handle converting version 1 configuration files. */
+ if (g_str_equal (version, "1"))
+ {
+ g_set_error_literal (error,
+ META_MONITOR_CONFIG_STORE_ERROR,
+ META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION,
+ "monitors.xml has the old format");
+ return;
+ }
if (!g_str_equal (version, QUOTE (MONITORS_CONFIG_XML_FORMAT_VERSION)))
{
@@ -212,22 +234,40 @@ handle_start_element (GMarkupParseContext *context,
}
parser->state = STATE_CONFIGURATION;
+ parser->current_was_migrated = FALSE;
+
return;
}
case STATE_CONFIGURATION:
{
- if (!g_str_equal (element_name, "logicalmonitor"))
+ if (g_str_equal (element_name, "logicalmonitor"))
+ {
+ parser->current_logical_monitor_config =
+ g_new0 (MetaLogicalMonitorConfig, 1);
+
+ parser->state = STATE_LOGICAL_MONITOR;
+ }
+ else if (g_str_equal (element_name, "migrated"))
+ {
+ parser->current_was_migrated = TRUE;
+
+ parser->state = STATE_MIGRATED;
+ }
+ else
{
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
"Invalid configuration element '%s'", element_name);
return;
}
- parser->current_logical_monitor_config =
- g_new0 (MetaLogicalMonitorConfig, 1);
+ return;
+ }
- parser->state = STATE_LOGICAL_MONITOR;
+ case STATE_MIGRATED:
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unexpected element '%s'", element_name);
return;
}
@@ -603,7 +643,9 @@ handle_end_element (GMarkupParseContext *context,
g_assert (g_str_equal (element_name, "logicalmonitor"));
- if (logical_monitor_config->scale == 0)
+ if (parser->current_was_migrated)
+ logical_monitor_config->scale = -1;
+ else if (logical_monitor_config->scale == 0)
logical_monitor_config->scale = 1;
parser->current_logical_monitor_configs =
@@ -615,17 +657,29 @@ handle_end_element (GMarkupParseContext *context,
return;
}
+ case STATE_MIGRATED:
+ {
+ g_assert (g_str_equal (element_name, "migrated"));
+
+ parser->state = STATE_CONFIGURATION;
+ return;
+ }
+
case STATE_CONFIGURATION:
{
MetaMonitorConfigStore *store = parser->config_store;
MetaMonitorsConfig *config;
GList *l;
MetaLogicalMonitorLayoutMode layout_mode;
+ MetaMonitorsConfigFlag config_flags = META_MONITORS_CONFIG_FLAG_NONE;
g_assert (g_str_equal (element_name, "configuration"));
- layout_mode =
- meta_monitor_manager_get_default_layout_mode (store->monitor_manager);
+ if (parser->current_was_migrated)
+ layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
+ else
+ layout_mode =
+ meta_monitor_manager_get_default_layout_mode (store->monitor_manager);
for (l = parser->current_logical_monitor_configs; l; l = l->next)
{
@@ -643,9 +697,13 @@ handle_end_element (GMarkupParseContext *context,
return;
}
+ if (parser->current_was_migrated)
+ config_flags |= META_MONITORS_CONFIG_FLAG_MIGRATED;
+
config =
meta_monitors_config_new (parser->current_logical_monitor_configs,
- layout_mode);
+ layout_mode,
+ config_flags);
parser->current_logical_monitor_configs = NULL;
@@ -785,6 +843,7 @@ handle_text (GMarkupParseContext *context,
case STATE_INITIAL:
case STATE_MONITORS:
case STATE_CONFIGURATION:
+ case STATE_MIGRATED:
case STATE_LOGICAL_MONITOR:
case STATE_MONITOR:
case STATE_MONITOR_SPEC:
@@ -1094,6 +1153,7 @@ append_transform (GString *buffer,
static void
append_logical_monitor_xml (GString *buffer,
+ MetaMonitorsConfig *config,
MetaLogicalMonitorConfig *logical_monitor_config)
{
char scale_str[G_ASCII_DTOSTR_BUF_SIZE];
@@ -1105,8 +1165,9 @@ append_logical_monitor_xml (GString *buffer,
logical_monitor_config->layout.y);
g_ascii_dtostr (scale_str, G_ASCII_DTOSTR_BUF_SIZE,
logical_monitor_config->scale);
- g_string_append_printf (buffer, " %s\n",
- scale_str);
+ if ((config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED) == 0)
+ g_string_append_printf (buffer, " %s\n",
+ scale_str);
if (logical_monitor_config->is_primary)
g_string_append (buffer, " yes\n");
if (logical_monitor_config->is_presentation)
@@ -1134,11 +1195,14 @@ generate_config_xml (MetaMonitorConfigStore *config_store)
g_string_append (buffer, " \n");
+ if (config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED)
+ g_string_append (buffer, " \n");
+
for (l = config->logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
- append_logical_monitor_xml (buffer, logical_monitor_config);
+ append_logical_monitor_xml (buffer, config, logical_monitor_config);
}
g_string_append (buffer, " \n");
@@ -1204,6 +1268,31 @@ meta_monitor_config_store_save (MetaMonitorConfigStore *config_store)
.buffer = buffer
};
+ /*
+ * Custom write file is only ever used by the test suite, and the test suite
+ * will want to have be able to read back the content immediately, so for
+ * custom write files, do the content replacement synchronously.
+ */
+ if (config_store->custom_write_file)
+ {
+ GError *error = NULL;
+
+ if (!g_file_replace_contents (config_store->custom_write_file,
+ buffer->str, buffer->len,
+ NULL,
+ FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL,
+ NULL,
+ &error))
+ {
+ g_warning ("Saving monitor configuration failed: %s\n",
+ error->message);
+ g_error_free (error);
+ }
+ return;
+ }
+
g_file_replace_contents_async (config_store->user_file,
buffer->str, buffer->len,
NULL,
@@ -1213,6 +1302,18 @@ meta_monitor_config_store_save (MetaMonitorConfigStore *config_store)
saved_cb, data);
}
+static void
+maybe_save_configs (MetaMonitorConfigStore *config_store)
+{
+ /*
+ * If a custom file is used, it means we are run by the test suite. When this
+ * is done, avoid replacing the user configuration file with test data,
+ * except if a custom write file is set as well.
+ */
+ if (!config_store->custom_read_file || config_store->custom_write_file)
+ meta_monitor_config_store_save (config_store);
+}
+
void
meta_monitor_config_store_add (MetaMonitorConfigStore *config_store,
MetaMonitorsConfig *config)
@@ -1220,21 +1321,33 @@ meta_monitor_config_store_add (MetaMonitorConfigStore *config_store,
g_hash_table_replace (config_store->configs,
config->key, g_object_ref (config));
- if (!config_store->custom_file)
- meta_monitor_config_store_save (config_store);
+ maybe_save_configs (config_store);
+}
+
+void
+meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store,
+ MetaMonitorsConfig *config)
+{
+ g_hash_table_remove (config_store->configs, config->key);
+
+ maybe_save_configs (config_store);
}
gboolean
meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
- const char *path,
+ const char *read_path,
+ const char *write_path,
GError **error)
{
- g_clear_object (&config_store->custom_file);
+ g_clear_object (&config_store->custom_read_file);
+ g_clear_object (&config_store->custom_write_file);
g_hash_table_remove_all (config_store->configs);
- config_store->custom_file = g_file_new_for_path (path);
+ config_store->custom_read_file = g_file_new_for_path (read_path);
+ if (write_path)
+ config_store->custom_write_file = g_file_new_for_path (write_path);
- return read_config_file (config_store, config_store->custom_file, error);
+ return read_config_file (config_store, config_store->custom_read_file, error);
}
int
@@ -1243,6 +1356,12 @@ meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store
return (int) g_hash_table_size (config_store->configs);
}
+MetaMonitorManager *
+meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store)
+{
+ return config_store->monitor_manager;
+}
+
MetaMonitorConfigStore *
meta_monitor_config_store_new (MetaMonitorManager *monitor_manager)
{
@@ -1259,7 +1378,7 @@ meta_monitor_config_store_constructed (GObject *object)
GError *error = NULL;
user_file_path = g_build_filename (g_get_user_config_dir (),
- "monitors-experimental.xml",
+ "monitors.xml",
NULL);
config_store->user_file = g_file_new_for_path (user_file_path);
@@ -1267,9 +1386,23 @@ meta_monitor_config_store_constructed (GObject *object)
{
if (!read_config_file (config_store, config_store->user_file, &error))
{
- g_warning ("Failed to read monitors config file '%s': %s",
- user_file_path, error->message);
- g_error_free (error);
+ if (error->domain == META_MONITOR_CONFIG_STORE_ERROR &&
+ error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)
+ {
+ g_clear_error (&error);
+ if (!meta_migrate_old_user_monitors_config (config_store, &error))
+ {
+ g_warning ("Failed to migrate old monitors config file: %s",
+ error->message);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ g_warning ("Failed to read monitors config file '%s': %s",
+ user_file_path, error->message);
+ g_error_free (error);
+ }
}
}
@@ -1284,7 +1417,8 @@ meta_monitor_config_store_dispose (GObject *object)
g_clear_pointer (&config_store->configs, g_hash_table_destroy);
g_clear_object (&config_store->user_file);
- g_clear_object (&config_store->custom_file);
+ g_clear_object (&config_store->custom_read_file);
+ g_clear_object (&config_store->custom_write_file);
G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->dispose (object);
}
diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h
index 7451fc11e..76f97e50d 100644
--- a/src/backends/meta-monitor-config-store.h
+++ b/src/backends/meta-monitor-config-store.h
@@ -38,10 +38,16 @@ MetaMonitorsConfig * meta_monitor_config_store_lookup (MetaMonitorConfigStore *c
void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store,
MetaMonitorsConfig *config);
+void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store,
+ MetaMonitorsConfig *config);
+
gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store,
- const char *path,
+ const char *read_path,
+ const char *write_path,
GError **error);
int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store);
+MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store);
+
#endif /* META_MONITOR_CONFIG_STORE_H */
diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c
index a4975fb0b..4258f71fa 100644
--- a/src/backends/meta-monitor-manager.c
+++ b/src/backends/meta-monitor-manager.c
@@ -429,6 +429,9 @@ meta_monitor_manager_apply_monitors_config (MetaMonitorManager *manager,
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
+ g_assert (!config ||
+ !(config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED));
+
if (!manager_class->apply_monitors_config (manager, config, method, error))
return FALSE;
@@ -1874,7 +1877,8 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet
logical_monitor_config);
}
- config = meta_monitors_config_new (logical_monitor_configs, layout_mode);
+ config = meta_monitors_config_new (logical_monitor_configs, layout_mode,
+ META_MONITORS_CONFIG_FLAG_NONE);
if (!meta_verify_monitors_config (config, manager, &error))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c
index 354f4a7c7..b063a552d 100644
--- a/src/tests/monitor-test-utils.c
+++ b/src/tests/monitor-test-utils.c
@@ -42,6 +42,7 @@ set_custom_monitor_config (const char *filename)
path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs",
filename, NULL);
- if (!meta_monitor_config_store_set_custom (config_store, path, &error))
+ if (!meta_monitor_config_store_set_custom (config_store, path, NULL,
+ &error))
g_error ("Failed to set custom config: %s", error->message);
}