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