summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorAleksey Samoilov <[email protected]>2026-08-12 14:16:28 +0400
committerLuke from DC <[email protected]>2026-08-19 19:15:44 +0000
commit0d64220932c78f5094eda04e9cac341f7f9bdd13 (patch)
treef8172188de2d67d4c5aacad7b92af1a163f87a52 /plugins
parent82c1a8a60564890ccb812274d8da70e00a3cf83b (diff)
downloadmate-settings-daemon-0d64220932c78f5094eda04e9cac341f7f9bdd13.tar.bz2
mate-settings-daemon-0d64220932c78f5094eda04e9cac341f7f9bdd13.tar.xz
wlrandr: persist the applied configuration instead of a live snapshot
Diffstat (limited to 'plugins')
-rw-r--r--plugins/wlrandr/msd-wlrandr-manager.c139
1 files changed, 130 insertions, 9 deletions
diff --git a/plugins/wlrandr/msd-wlrandr-manager.c b/plugins/wlrandr/msd-wlrandr-manager.c
index 837fa15..3349e3d 100644
--- a/plugins/wlrandr/msd-wlrandr-manager.c
+++ b/plugins/wlrandr/msd-wlrandr-manager.c
@@ -181,7 +181,7 @@ static void
emit_configuration_changed (MsdWlrandrManager *manager);
static void
-save_current_configuration (MsdWlrandrManager *manager);
+save_current_configuration (MsdWlrandrManager *manager, WlrConfig *config);
static void
queue_rollback_confirmation (MsdWlrandrManager *manager, WlrConfig *previous);
@@ -581,6 +581,7 @@ typedef struct {
gboolean save;
gboolean confirm_rollback;
WlrConfig *previous;
+ WlrConfig *config;
WlOutputHead *adaptive_sync_head;
} ApplyData;
@@ -607,7 +608,7 @@ config_succeeded_handler (void *data, struct zwlr_output_configuration_v1 *confi
g_debug ("msd-wlrandr: output configuration applied");
if (ad->save)
- save_current_configuration (ad->manager);
+ save_current_configuration (ad->manager, ad->config);
if (ad->confirm_rollback)
queue_rollback_confirmation (ad->manager, ad->previous);
@@ -617,6 +618,7 @@ config_succeeded_handler (void *data, struct zwlr_output_configuration_v1 *confi
ad->manager->priv->pending_config = NULL;
g_idle_add (probe_idle_cb, ad->manager);
zwlr_output_configuration_v1_destroy (config);
+ wlr_config_free (ad->config);
g_free (ad);
}
@@ -632,7 +634,11 @@ config_failed_handler (void *data, struct zwlr_output_configuration_v1 *config)
g_debug ("msd-wlrandr: adaptive sync rejected by the compositor on %s", head->name);
head->adaptive_sync_supported = 0;
head->adaptive_sync = 0;
- save_current_configuration (ad->manager);
+
+ /* The configuration was rejected, so nothing was applied and
+ * the live head state is unchanged; persist it as is.
+ */
+ save_current_configuration (ad->manager, NULL);
rebuild_menu (ad->manager);
} else {
notify_config_error (_("Could not apply the display configuration"));
@@ -642,6 +648,7 @@ config_failed_handler (void *data, struct zwlr_output_configuration_v1 *config)
ad->manager->priv->pending_config = NULL;
g_idle_add (probe_idle_cb, ad->manager);
zwlr_output_configuration_v1_destroy (config);
+ wlr_config_free (ad->config);
g_free (ad);
}
@@ -656,6 +663,7 @@ config_cancelled_handler (void *data, struct zwlr_output_configuration_v1 *confi
ad->manager->priv->pending_config = NULL;
g_idle_add (probe_idle_cb, ad->manager);
zwlr_output_configuration_v1_destroy (config);
+ wlr_config_free (ad->config);
g_free (ad);
}
@@ -912,6 +920,50 @@ fill_output_mode (WlOutputHead *head, WlrOutputInfo *info)
}
}
+/* Deep copy of a configuration. Used to keep a reference to the
+ * configuration that was actually sent to the compositor, so that the file
+ * is saved with the intended values rather than whatever the compositor
+ * last reported (which may not have been updated yet when the
+ * "succeeded" event arrives).
+ */
+static WlrConfig *
+wlr_config_copy (const WlrConfig *config)
+{
+ WlrConfig *copy;
+ GPtrArray *outputs;
+ int i;
+
+ if (config == NULL)
+ return NULL;
+
+ outputs = g_ptr_array_new ();
+ for (i = 0; config->outputs != NULL && config->outputs[i] != NULL; i++) {
+ WlrOutputInfo *src = config->outputs[i];
+ WlrOutputInfo *dst = wlr_output_info_new (src->name);
+
+ dst->vendor = g_strdup (src->vendor);
+ dst->product = g_strdup (src->product);
+ dst->serial = g_strdup (src->serial);
+ dst->enabled = src->enabled;
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->rate = src->rate;
+ dst->x = src->x;
+ dst->y = src->y;
+ dst->scale = src->scale;
+ dst->transform = src->transform;
+ dst->adaptive_sync = src->adaptive_sync;
+
+ g_ptr_array_add (outputs, dst);
+ }
+ g_ptr_array_add (outputs, NULL);
+
+ copy = g_new0 (WlrConfig, 1);
+ copy->outputs = (WlrOutputInfo **) g_ptr_array_free (outputs, FALSE);
+
+ return copy;
+}
+
static gboolean
apply_configuration (MsdWlrandrManager *manager,
WlrConfig *config,
@@ -973,6 +1025,26 @@ apply_configuration (MsdWlrandrManager *manager,
ad->previous = previous;
ad->adaptive_sync_head = adaptive_sync_head;
+ ad->config = wlr_config_copy (config);
+
+ /* Enrich the copy with the head identity fields; configurations that
+ * arrive over D-Bus do not carry them and they are useful when the
+ * file is inspected by hand.
+ */
+ for (l = priv->heads; l != NULL; l = l->next) {
+ WlOutputHead *head = l->data;
+ WlrOutputInfo *info = find_output_in_config (ad->config, head->name);
+
+ if (info == NULL)
+ continue;
+ if (info->vendor == NULL || *info->vendor == '\0')
+ info->vendor = g_strdup (head->make);
+ if (info->product == NULL || *info->product == '\0')
+ info->product = g_strdup (head->model);
+ if (info->serial == NULL || *info->serial == '\0')
+ info->serial = g_strdup (head->serial_number);
+ }
+
zwlr_output_configuration_v1_add_listener (config_obj, &config_listener, ad);
priv->pending_config = config_obj;
zwlr_output_configuration_v1_apply (config_obj);
@@ -1190,6 +1262,34 @@ yes_no_to_boolean (const char *text)
return FALSE;
}
+/* Read a scale value from the configuration file. g_ascii_strtod() is
+ * locale-independent and only accepts '.' as the decimal separator, so
+ * normalize any ',' left in the file by older versions of the plugin that
+ * wrote it with a locale-dependent "%f". Without this, e.g. "1,500000"
+ * would silently come back as 1.0.
+ */
+static double
+parse_scale (const char *text)
+{
+ char *normalized;
+ char *p;
+ double value;
+
+ if (text == NULL)
+ return 1.0;
+
+ normalized = g_strdup (text);
+ for (p = normalized; *p != '\0'; p++) {
+ if (*p == ',')
+ *p = '.';
+ }
+
+ value = g_ascii_strtod (normalized, NULL);
+ g_free (normalized);
+
+ return value;
+}
+
static const char *
transform_to_name (int transform)
{
@@ -1332,7 +1432,7 @@ parser_end_element (GMarkupParseContext *context,
} else if (strcmp (parser->current_element, "y") == 0) {
output->y = atoi (text);
} else if (strcmp (parser->current_element, "scale") == 0) {
- output->scale = g_ascii_strtod (text, NULL);
+ output->scale = parse_scale (text);
} else if (strcmp (parser->current_element, "transform") == 0) {
output->transform = transform_from_name (text);
} else if (strcmp (parser->current_element, "adaptive_sync") == 0) {
@@ -1512,7 +1612,19 @@ emit_configuration (WlrConfig *config, GString *string)
g_string_append_printf (string, " <rate>%d</rate>\n", output->rate);
g_string_append_printf (string, " <x>%d</x>\n", output->x);
g_string_append_printf (string, " <y>%d</y>\n", output->y);
- g_string_append_printf (string, " <scale>%.6f</scale>\n", output->scale);
+
+ {
+ /* "%f" is locale-dependent and would write
+ * e.g. "1,500000" in comma-decimal locales,
+ * while the parser always reads with the
+ * locale-independent g_ascii_strtod(). Use
+ * g_ascii_formatd() so the file is always
+ * written with a '.' decimal separator.
+ */
+ char scale_buf[G_ASCII_DTOSTR_BUF_SIZE];
+ g_ascii_formatd (scale_buf, sizeof (scale_buf), "%f", output->scale);
+ g_string_append_printf (string, " <scale>%s</scale>\n", scale_buf);
+ }
g_string_append_printf (string, " <transform>%s</transform>\n", transform_to_name (output->transform));
} else {
g_string_append (string, " <enabled>no</enabled>\n");
@@ -1540,11 +1652,16 @@ configurations_match_current (WlrConfig **configs, GList *heads)
return FALSE;
}
+/* Persist the current output configuration to monitors-wayland.xml. If
+ * `config` is non-NULL it is used as-is; otherwise the live head state is
+ * snapshotted (only used when nothing was applied and the state is known to
+ * be accurate, e.g. after a rejected adaptive sync toggle).
+ */
static void
-save_current_configuration (MsdWlrandrManager *manager)
+save_current_configuration (MsdWlrandrManager *manager, WlrConfig *config)
{
struct MsdWlrandrManagerPrivate *priv = manager->priv;
- WlrConfig *config;
+ WlrConfig *snapshot;
WlrConfig **existing;
GString *string;
char *intended;
@@ -1552,7 +1669,10 @@ save_current_configuration (MsdWlrandrManager *manager)
int i;
GError *error = NULL;
- config = snapshot_config (manager);
+ if (config == NULL)
+ config = snapshot = snapshot_config (manager);
+ else
+ snapshot = NULL;
intended = g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
backup = g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
@@ -1584,7 +1704,8 @@ save_current_configuration (MsdWlrandrManager *manager)
g_string_free (string, TRUE);
g_free (intended);
g_free (backup);
- wlr_config_free (config);
+ if (snapshot != NULL)
+ wlr_config_free (snapshot);
}
static gboolean