[gnome-flashback] backends: migrate old monitor configuration files
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback] backends: migrate old monitor configuration files
- Date: Thu, 21 Sep 2017 00:08:49 +0000 (UTC)
commit 925773fdea11b0387c3c708a254db504c1bfa594
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date: Wed Sep 20 22:44:26 2017 +0300
backends: migrate old monitor configuration files
backends/gf-monitor-config-migration.c | 1184 +++++++++++++++++++++++++++++++-
backends/gf-rectangle.c | 10 +
backends/gf-rectangle.h | 3 +
3 files changed, 1191 insertions(+), 6 deletions(-)
---
diff --git a/backends/gf-monitor-config-migration.c b/backends/gf-monitor-config-migration.c
index 3c4902c..f0aaa81 100644
--- a/backends/gf-monitor-config-migration.c
+++ b/backends/gf-monitor-config-migration.c
@@ -38,25 +38,1162 @@
#include "config.h"
#include <gio/gio.h>
+#include <string.h>
+#include "gf-monitor-config-manager-private.h"
#include "gf-monitor-config-migration-private.h"
#include "gf-monitor-config-store-private.h"
+#include "gf-monitor-spec-private.h"
+
+typedef struct
+{
+ gchar *connector;
+ gchar *vendor;
+ gchar *product;
+ gchar *serial;
+} GfOutputKey;
+
+typedef struct
+{
+ gboolean enabled;
+ GfRectangle rect;
+ gfloat refresh_rate;
+ GfMonitorTransform transform;
+
+ gboolean is_primary;
+ gboolean is_presentation;
+ gboolean is_underscanning;
+} GfOutputConfig;
+
+typedef struct
+{
+ GfOutputKey *keys;
+ GfOutputConfig *outputs;
+ guint n_outputs;
+} GfLegacyMonitorsConfig;
+
+enum
+{
+ GF_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
+ GF_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE
+} GfConfigMigrationError;
+
+#define GF_MONITORS_CONFIG_MIGRATION_ERROR (gf_monitors_config_migration_error_quark ())
+static GQuark gf_monitors_config_migration_error_quark (void);
+
+G_DEFINE_QUARK (gf-monitors-config-migration-error-quark,
+ gf_monitors_config_migration_error)
+
+typedef struct
+{
+ GfOutputKey *output_key;
+ GfOutputConfig *output_config;
+} MonitorTile;
+
+typedef enum
+{
+ STATE_INITIAL,
+ STATE_MONITORS,
+ STATE_CONFIGURATION,
+ STATE_OUTPUT,
+ STATE_OUTPUT_FIELD,
+ STATE_CLONE
+} ParserState;
+
+typedef struct
+{
+ ParserState state;
+ gint unknown_count;
+
+ GArray *key_array;
+ GArray *output_array;
+ GfOutputKey key;
+ GfOutputConfig output;
+
+ gchar *output_field;
+
+ GHashTable *configs;
+} ConfigParser;
+
+static GfLegacyMonitorsConfig *
+legacy_config_new (void)
+{
+ return g_new0 (GfLegacyMonitorsConfig, 1);
+}
+
+static void
+legacy_config_free (gpointer data)
+{
+ GfLegacyMonitorsConfig *config = data;
+
+ g_free (config->keys);
+ g_free (config->outputs);
+ g_free (config);
+}
+
+static gulong
+output_key_hash (const GfOutputKey *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 GfOutputKey *one,
+ const GfOutputKey *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 guint
+legacy_config_hash (gconstpointer data)
+{
+ const GfLegacyMonitorsConfig *config = data;
+ guint 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 GfLegacyMonitorsConfig *c_one = one;
+ const GfLegacyMonitorsConfig *c_two = two;
+ guint 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 (GfOutputKey *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 gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_INITIAL:
+ {
+ gchar *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 (GfOutputKey));
+ parser->output_array = g_array_new (FALSE, FALSE,
+ sizeof (GfOutputConfig));
+ 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)
+ {
+ gchar *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 (GfOutputKey));
+ memset (&parser->output, 0, sizeof (GfOutputConfig));
+
+ 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 gchar *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)
+ {
+ GfLegacyMonitorsConfig *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 (GfOutputKey));
+ memset (&parser->output, 0, sizeof (GfOutputConfig));
+
+ 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 gchar *text,
+ gsize text_len,
+ gint *field,
+ GError **error)
+{
+ gchar buf[64];
+ gint64 v;
+ gchar *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 gchar *text,
+ gsize text_len,
+ gfloat *field,
+ GError **error)
+{
+ gchar buf[64];
+ gfloat v;
+ gchar *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 gchar *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 gchar *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 = GF_MONITOR_TRANSFORM_NORMAL;
+ else if (strncmp (text, "left", text_len) == 0)
+ parser->output.transform = GF_MONITOR_TRANSFORM_90;
+ else if (strncmp (text, "upside_down", text_len) == 0)
+ parser->output.transform = GF_MONITOR_TRANSFORM_180;
+ else if (strncmp (text, "right", text_len) == 0)
+ parser->output.transform = GF_MONITOR_TRANSFORM_270;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid rotation type %.*s", (gint) text_len, text);
+ }
+ else if (strcmp (parser->output_field, "reflect_x") == 0)
+ parser->output.transform += read_bool (text, text_len, error) ? GF_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)
+{
+ gchar *contents;
+ gsize size;
+ ConfigParser parser = { 0 };
+ GMarkupParseContext *context;
+
+ contents = NULL;
+ if (!g_file_load_contents (file, NULL, &contents, &size, NULL, error))
+ return FALSE;
+
+ parser.state = STATE_INITIAL;
+ parser.unknown_count = 0;
+
+ parser.configs = g_hash_table_new_full (legacy_config_hash,
+ legacy_config_equal,
+ legacy_config_free,
+ NULL);
+
+ 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_free (parser.output_field);
+
+ g_hash_table_destroy (parser.configs);
+
+ g_markup_parse_context_free (context);
+ g_free (contents);
+
+ return NULL;
+ }
+
+ g_markup_parse_context_free (context);
+ g_free (contents);
+
+ return parser.configs;
+}
+
+static GfMonitorConfig *
+create_monitor_config (GfOutputKey *output_key,
+ GfOutputConfig *output_config,
+ gint mode_width,
+ gint mode_height,
+ GError **error)
+{
+ GfMonitorModeSpec *mode_spec;
+ GfMonitorSpec *monitor_spec;
+ GfMonitorConfig *monitor_config;
+
+ mode_spec = g_new0 (GfMonitorModeSpec, 1);
+
+ mode_spec->width = mode_width;
+ mode_spec->height = mode_height;
+ mode_spec->refresh_rate = output_config->refresh_rate;
+ mode_spec->flags = GF_CRTC_MODE_FLAG_NONE;
+
+ if (!gf_verify_monitor_mode_spec (mode_spec, error))
+ {
+ g_free (mode_spec);
+ return NULL;
+ }
+
+ monitor_spec = g_new0 (GfMonitorSpec, 1);
+
+ monitor_spec->connector = output_key->connector;
+ monitor_spec->vendor = output_key->vendor;
+ monitor_spec->product = output_key->product;
+ monitor_spec->serial = output_key->serial;
+
+ monitor_config = g_new0 (GfMonitorConfig, 1);
+
+ monitor_config->monitor_spec = monitor_spec;
+ monitor_config->mode_spec = mode_spec;
+ monitor_config->enable_underscanning = output_config->is_underscanning;
+
+ if (!gf_verify_monitor_config (monitor_config, error))
+ {
+ gf_monitor_config_free (monitor_config);
+ return NULL;
+ }
+
+ return monitor_config;
+}
+
+static GfMonitorConfig *
+try_derive_tiled_monitor_config (GfLegacyMonitorsConfig *config,
+ GfOutputKey *output_key,
+ GfOutputConfig *output_config,
+ GfMonitorConfigStore *config_store,
+ GfRectangle *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 };
+ GfMonitorTransform transform = output_config->transform;
+ guint i;
+ gint max_x = 0;
+ gint min_x = INT_MAX;
+ gint max_y = 0;
+ gint min_y = INT_MAX;
+ gint mode_width = 0;
+ gint mode_height = 0;
+ GfMonitorConfig *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++)
+ {
+ GfOutputKey *other_output_key = &config->keys[i];
+ GfOutputConfig *other_output_config = &config->outputs[i];
+ GfRectangle *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,
+ GF_MONITORS_CONFIG_MIGRATION_ERROR,
+ GF_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
+ "Not a tiled monitor");
+
+ return NULL;
+ }
+
+ switch (transform)
+ {
+ case GF_MONITOR_TRANSFORM_NORMAL:
+ origin_tile = top_left_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+
+ case GF_MONITOR_TRANSFORM_90:
+ origin_tile = bottom_left_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+
+ case GF_MONITOR_TRANSFORM_180:
+ origin_tile = bottom_right_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+
+ case GF_MONITOR_TRANSFORM_270:
+ origin_tile = top_right_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+
+ case GF_MONITOR_TRANSFORM_FLIPPED:
+ origin_tile = bottom_left_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+
+ case GF_MONITOR_TRANSFORM_FLIPPED_90:
+ origin_tile = bottom_right_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+
+ case GF_MONITOR_TRANSFORM_FLIPPED_180:
+ origin_tile = top_right_tile;
+ mode_width = max_x - min_x;
+ mode_height = max_y - min_y;
+ break;
+
+ case GF_MONITOR_TRANSFORM_FLIPPED_270:
+ origin_tile = top_left_tile;
+ mode_width = max_y - min_y;
+ mode_height = max_x - min_x;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ 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,
+ GF_MONITORS_CONFIG_MIGRATION_ERROR,
+ GF_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 = (GfRectangle) {
+ .x = min_x,
+ .y = min_y,
+ .width = max_x - min_x,
+ .height = max_y - min_y
+ };
+
+ return monitor_config;
+}
+
+static GfMonitorConfig *
+derive_monitor_config (GfOutputKey *output_key,
+ GfOutputConfig *output_config,
+ GfRectangle *out_layout,
+ GError **error)
+{
+ gint mode_width;
+ gint mode_height;
+ GfMonitorConfig *monitor_config;
+
+ if (gf_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 GfLogicalMonitorConfig *
+ensure_logical_monitor (GList **logical_monitor_configs,
+ GfOutputConfig *output_config,
+ GfRectangle *layout)
+{
+ GfLogicalMonitorConfig *new_logical_monitor_config;
+ GList *l;
+
+ for (l = *logical_monitor_configs; l; l = l->next)
+ {
+ GfLogicalMonitorConfig *logical_monitor_config = l->data;
+
+ if (gf_rectangle_equal (&logical_monitor_config->layout, layout))
+ return logical_monitor_config;
+ }
+
+ new_logical_monitor_config = g_new0 (GfLogicalMonitorConfig, 1);
+
+ new_logical_monitor_config->layout = *layout;
+ new_logical_monitor_config->monitor_configs = NULL;
+ new_logical_monitor_config->transform = output_config->transform;
+ new_logical_monitor_config->scale = -1.0;
+ new_logical_monitor_config->is_primary = output_config->is_primary;
+ new_logical_monitor_config->is_presentation = output_config->is_presentation;
+
+ *logical_monitor_configs = g_list_append (*logical_monitor_configs,
+ new_logical_monitor_config);
+
+ return new_logical_monitor_config;
+}
+
+static GList *
+derive_logical_monitor_configs (GfLegacyMonitorsConfig *config,
+ GfMonitorConfigStore *config_store,
+ GError **error)
+{
+ GList *logical_monitor_configs;
+ guint i;
+
+ logical_monitor_configs = NULL;
+
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ GfOutputKey *output_key;
+ GfOutputConfig *output_config;
+ GfMonitorConfig *monitor_config;
+ GfRectangle layout;
+ GfLogicalMonitorConfig *logical_monitor_config;
+
+ output_key = &config->keys[i];
+ output_config = &config->outputs[i];
+ monitor_config = NULL;
+
+ 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 == GF_MONITORS_CONFIG_MIGRATION_ERROR)
+ {
+ gint error_code = (*error)->code;
+
+ g_clear_error (error);
+
+ switch (error_code)
+ {
+ case GF_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED:
+ break;
+
+ case GF_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE:
+ default:
+ continue;
+ }
+ }
+ else
+ {
+ g_list_free_full (logical_monitor_configs,
+ (GDestroyNotify) gf_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 (logical_monitor_configs,
+ (GDestroyNotify) gf_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 (GfLegacyMonitorsConfig *config)
+{
+ gchar **output_strings;
+ guint i;
+ gchar *key_name;
+
+ output_strings = g_new0 (gchar *, config->n_outputs + 1);
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ GfOutputKey *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)
+{
+ GfLegacyMonitorsConfig *legacy_config;
+ GfMonitorConfigStore *config_store;
+ GfMonitorManager *monitor_manager;
+ GList *logical_monitor_configs;
+ GError *error;
+ GfLogicalMonitorLayoutMode layout_mode;
+ GfMonitorsConfig *config;
+
+ legacy_config = key;
+ config_store = user_data;
+ monitor_manager = gf_monitor_config_store_get_monitor_manager (config_store);
+
+ error = NULL;
+ logical_monitor_configs = derive_logical_monitor_configs (legacy_config,
+ config_store,
+ &error);
+
+ if (!logical_monitor_configs)
+ {
+ gchar *config_name;
+
+ config_name = generate_config_name (legacy_config);
+ g_warning ("Failed to migrate monitor configuration for %s: %s",
+ config_name, error->message);
+
+ g_free (config_name);
+ return;
+ }
+
+ layout_mode = GF_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
+ config = gf_monitors_config_new (logical_monitor_configs, layout_mode,
+ GF_MONITORS_CONFIG_FLAG_MIGRATED);
+
+ if (!gf_verify_monitors_config (config, monitor_manager, &error))
+ {
+ gchar *config_name;
+
+ config_name = generate_config_name (legacy_config);
+ g_warning ("Ignoring invalid monitor configuration for %s: %s",
+ config_name, error->message);
+
+ g_free (config_name);
+ g_object_unref (config);
+ return;
+ }
+
+ gf_monitor_config_store_add (config_store, config);
+}
gboolean
gf_migrate_old_monitors_config (GfMonitorConfigStore *config_store,
GFile *in_file,
GError **error)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not implemented");
- return FALSE;
+ GHashTable *configs;
+
+ 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
gf_migrate_old_user_monitors_config (GfMonitorConfigStore *config_store,
GError **error)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not implemented");
- return FALSE;
+ const gchar *config_dir;
+ gchar *user_file_path;
+ GFile *user_file;
+ gchar *backup_path;
+ GFile *backup_file;
+ gboolean migrated;
+
+ config_dir = g_get_user_config_dir ();
+
+ user_file_path = g_build_filename (config_dir, "monitors.xml", NULL);
+ user_file = g_file_new_for_path (user_file_path);
+
+ backup_path = g_build_filename (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);
+ }
+
+ migrated = gf_migrate_old_monitors_config (config_store, user_file, error);
+
+ g_free (user_file_path);
+ g_object_unref (user_file);
+
+ g_free (backup_path);
+ g_object_unref (backup_file);
+
+ return migrated;
}
gboolean
@@ -64,6 +1201,41 @@ gf_finish_monitors_config_migration (GfMonitorManager *monitor_manager,
GfMonitorsConfig *config,
GError **error)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not implemented");
- return FALSE;
+ GfMonitorConfigManager *config_manager;
+ GfMonitorConfigStore *config_store;
+ GList *l;
+
+ config_manager = monitor_manager->config_manager;
+ config_store = gf_monitor_config_manager_get_store (config_manager);
+
+ for (l = config->logical_monitor_configs; l; l = l->next)
+ {
+ GfLogicalMonitorConfig *logical_monitor_config;
+ GfMonitorConfig *monitor_config;
+ GfMonitorSpec *monitor_spec;
+ GfMonitor *monitor;
+ GfMonitorModeSpec *monitor_mode_spec;
+ GfMonitorMode *monitor_mode;
+ gfloat scale;
+
+ logical_monitor_config = l->data;
+ monitor_config = logical_monitor_config->monitor_configs->data;
+ monitor_spec = monitor_config->monitor_spec;
+ monitor = gf_monitor_manager_get_monitor_from_spec (monitor_manager, monitor_spec);
+ monitor_mode_spec = monitor_config->mode_spec;
+ monitor_mode = gf_monitor_get_mode_from_spec (monitor, monitor_mode_spec);
+ scale = gf_monitor_calculate_mode_scale (monitor, monitor_mode);
+
+ logical_monitor_config->scale = scale;
+ }
+
+ config->layout_mode = gf_monitor_manager_get_default_layout_mode (monitor_manager);
+ config->flags &= ~GF_MONITORS_CONFIG_FLAG_MIGRATED;
+
+ if (!gf_verify_monitors_config (config, monitor_manager, error))
+ return FALSE;
+
+ gf_monitor_config_store_add (config_store, config);
+
+ return TRUE;
}
diff --git a/backends/gf-rectangle.c b/backends/gf-rectangle.c
index 2699ff3..06ebbba 100644
--- a/backends/gf-rectangle.c
+++ b/backends/gf-rectangle.c
@@ -36,6 +36,16 @@ gf_rectangle_overlap (const GfRectangle *rect1,
}
gboolean
+gf_rectangle_equal (const GfRectangle *src1,
+ const GfRectangle *src2)
+{
+ return ((src1->x == src2->x) &&
+ (src1->y == src2->y) &&
+ (src1->width == src2->width) &&
+ (src1->height == src2->height));
+}
+
+gboolean
gf_rectangle_vert_overlap (const GfRectangle *rect1,
const GfRectangle *rect2)
{
diff --git a/backends/gf-rectangle.h b/backends/gf-rectangle.h
index 074370b..f587495 100644
--- a/backends/gf-rectangle.h
+++ b/backends/gf-rectangle.h
@@ -34,6 +34,9 @@ typedef struct
gint height;
} GfRectangle;
+gboolean gf_rectangle_equal (const GfRectangle *src1,
+ const GfRectangle *src2);
+
gboolean gf_rectangle_vert_overlap (const GfRectangle *rect1,
const GfRectangle *rect2);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]