From 1ea364c83051e9453c6f0eca47f6f1e3b1a38933 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Fri, 30 May 2025 20:15:52 -0300 Subject: [PATCH 01/19] Add gschema --- data/display.gschema.xml | 15 +++++++++++++++ data/meson.build | 6 ++++++ meson.build | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 data/display.gschema.xml diff --git a/data/display.gschema.xml b/data/display.gschema.xml new file mode 100644 index 00000000..405b702b --- /dev/null +++ b/data/display.gschema.xml @@ -0,0 +1,15 @@ + + + + + {} + Preferred display layouts + + Stores user-defined display layout profiles. + Each profile contains a unique identifier and a list of monitors, with their respective position (x, y) and other properties such as transformation (e.g., rotation). + This allows the system to restore or suggest preferred monitor arrangements and settings when displays are connected or configurations change. + + + + + \ No newline at end of file diff --git a/data/meson.build b/data/meson.build index bb32ce31..592d77a7 100644 --- a/data/meson.build +++ b/data/meson.build @@ -11,3 +11,9 @@ gresource = gnome.compile_resources( 'gresource', 'display.gresource.xml' ) + +install_data( + 'display.gschema.xml', + install_dir: datadir / 'glib-2.0' / 'schemas', + rename: 'io.elementary.settings.display.gschema.xml' +) \ No newline at end of file diff --git a/meson.build b/meson.build index cd420ed8..352622fe 100644 --- a/meson.build +++ b/meson.build @@ -30,3 +30,5 @@ config_file = configure_file( subdir('data') subdir('src') subdir('po') + +gnome.post_install(glib_compile_schemas: true) \ No newline at end of file From 762da1f86f7f5d8c53cf6a141dddcda2688578f0 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Fri, 30 May 2025 20:16:32 -0300 Subject: [PATCH 02/19] Implement MonitorLayoutManager --- src/Objects/MonitorLayoutManager.vala | 172 ++++++++++++++++++++++++++ src/Objects/MonitorLayoutProfile.vala | 48 +++++++ src/meson.build | 2 + 3 files changed, 222 insertions(+) create mode 100644 src/Objects/MonitorLayoutManager.vala create mode 100644 src/Objects/MonitorLayoutProfile.vala diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala new file mode 100644 index 00000000..a701a7f7 --- /dev/null +++ b/src/Objects/MonitorLayoutManager.vala @@ -0,0 +1,172 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. + * + * Authored by: Leonardo Lemos + */ + +public class Display.MonitorLayoutManager : GLib.Object { + private Settings settings; + + private const string PREFERRED_MONITOR_LAYOUTS_KEY = "preferred-display-layouts"; + + public MonitorLayoutManager () { + Object (); + } + + construct { + settings = new Settings ("io.elementary.settings.display"); + } + + public void arrange_monitors (Gee.LinkedList virtual_monitors) { + if (virtual_monitors.size == 1) { + // If there's only one monitor, no need to arrange + return; + } + + var layout_key = get_layout_key (virtual_monitors); + var layout = find_match_layout (layout_key); + var is_cloned = is_virtual_monitors_cloned (virtual_monitors); + var has_update = false; + + + if (layout != null) { + foreach (var virtual_monitor in virtual_monitors) { + var monitor_layout = layout.find_position_by_id (virtual_monitor.monitors[0].hash.to_string ()); + + if (monitor_layout != null) { + if ((virtual_monitor.x != monitor_layout.x || virtual_monitor.y != monitor_layout.y) && !is_cloned) { + has_update = true; + break; + } + + virtual_monitor.x = monitor_layout.x; + virtual_monitor.y = monitor_layout.y; + } + } + } else { + // If no layout found, we save the current layout to use later + save_layout (virtual_monitors); + } + + if (has_update) { + save_layout (virtual_monitors); + } + } + + public void save_layout (Gee.LinkedList virtual_monitors) { + var key = get_layout_key (virtual_monitors); + var layout_variant = build_layout_variant (virtual_monitors); + + var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); + + add_or_update_layout (layouts, key, layout_variant); + + } + + public MonitorLayoutProfile? find_match_layout (string key) { + // Layouts format is 'a{sa{sv}}' + var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); + + if (layouts == null || layouts.n_children () == 0) { + return null; // No layouts saved + } + + for (var i = 0; i < layouts.n_children (); i++) { + var layout = layouts.get_child_value (i); + var layout_key = layout.get_child_value (0).get_string (); + var monitors = layout.get_child_value (1); + + if (layout_key != key) { + continue; + } + + var virtual_monitor_layout = new MonitorLayoutProfile (layout_key); + + // Process the monitors in the layout + for (var j = 0; j < monitors.n_children (); j++) { + var monitor = monitors.get_child_value (j); + var monitor_props = monitor.get_child_value (1); + + warning (monitor_props.get_child_value (0).get_type ().dup_string ()); + warning (monitor_props.get_child_value (0).get_child_value (1).get_child_value (0).get_type ().dup_string ()); + + + virtual_monitor_layout.add_position ( + monitor.get_child_value (0).get_string (), // id + monitor_props.get_child_value (0).get_child_value (1).get_child_value (0).get_int32 (), // x position + monitor_props.get_child_value (1).get_child_value (1).get_child_value (0).get_int32 () // y position + ); + } + + return virtual_monitor_layout; + } + + return null; + } + + private string get_layout_key (Gee.LinkedList virtual_monitors) { + // Generate a unique key based on the virtual monitors' monitors hashes + var key = new StringBuilder (); + + foreach (var virtual_monitor in virtual_monitors) { + foreach (var monitor in virtual_monitor.monitors) { + key.append (monitor.hash.to_string ()); + } + } + + return key.str.hash ().to_string (); + } + + private GLib.Variant build_layout_variant (Gee.LinkedList virtual_monitors) { + var dict_builder = new VariantBuilder(VariantType.DICTIONARY); + + foreach (var monitor in virtual_monitors) { + var props_builder = new VariantBuilder(VariantType.DICTIONARY); + var key = monitor.monitors.get(0).hash.to_string(); + + props_builder.add_value (new Variant.dict_entry ("x", new Variant.variant(new Variant.int32 (monitor.x)))); + props_builder.add_value (new Variant.dict_entry ("y", new Variant.variant(new Variant.int32 (monitor.y)))); + + dict_builder.add_value (new Variant.dict_entry (key, props_builder)); + } + + return dict_builder.end (); + } + + private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { + var layout_builder = new VariantBuilder(VariantType.DICTIONARY); + bool found = false; + + for (var i = 0; i < layouts.n_children (); i++) { + var layout = layouts.get_child_value (i); + var layout_key = layout.get_child_value (0).get_string (); + + if (layout_key == key) { + // Update existing layout + layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + found = true; + } else { + // Keep existing layout + layout_builder.add_value (new Variant.dict_entry (layout_key, layout)); + } + } + + if (!found) { + // Add new layout + layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + } + + settings.set_value(PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); + } + + private bool is_virtual_monitors_cloned (Gee.LinkedList virtual_monitors) { + foreach (var monitor in virtual_monitors) { + if (monitor.x != 0 || monitor.y != 0) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Objects/MonitorLayoutProfile.vala b/src/Objects/MonitorLayoutProfile.vala new file mode 100644 index 00000000..543f0f8b --- /dev/null +++ b/src/Objects/MonitorLayoutProfile.vala @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. + * + * Authored by: Leonardo Lemos + */ + +public class Display.MonitorLayoutProfile : GLib.Object { + public class MonitorPosition : GLib.Object { + public string id { get; set; } + public int x { get; set; } + public int y { get; set; } + + public MonitorPosition (string id, int x, int y) { + Object (id: id, x: x, y: y); + } + } + + public string id { get; set; } + private List _positions; + + public unowned List positions { + get { + return _positions; + } + } + + public MonitorLayoutProfile (string id) { + Object (id: id); + } + + construct { + _positions = new List (); + } + + public void add_position (string id, int x, int y) { + _positions.append (new MonitorPosition (id, x, y)); + } + + public MonitorPosition? find_position_by_id (string id) { + foreach (var position in _positions) { + if (position.id == id) { + return position; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 94aa9310..6de3dd7f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -10,6 +10,8 @@ plug_files = files( 'Objects/MonitorMode.vala', 'Objects/MonitorManager.vala', 'Objects/Monitor.vala', + 'Objects/MonitorLayoutManager.vala', + 'Objects/MonitorLayoutProfile.vala', 'Views/NightLightView.vala', 'Views/DisplaysView.vala', 'Views' / 'FiltersView.vala', From dd6c098b31b28c48c47e0d67779a0ad238405d05 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Fri, 30 May 2025 20:16:52 -0300 Subject: [PATCH 03/19] Use MonitorLayoutManager --- src/Objects/MonitorManager.vala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index 7e04b0e4..98f9e21d 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -48,6 +48,7 @@ public class Display.MonitorManager : GLib.Object { private MutterDisplayConfigInterface iface; private uint current_serial; + private MonitorLayoutManager layout_manager; private static MonitorManager monitor_manager; public static unowned MonitorManager get_default () { @@ -65,6 +66,7 @@ public class Display.MonitorManager : GLib.Object { construct { monitors = new Gee.LinkedList (); virtual_monitors = new Gee.LinkedList (); + layout_manager = new MonitorLayoutManager (); try { iface = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.Mutter.DisplayConfig", "/org/gnome/Mutter/DisplayConfig"); iface.monitors_changed.connect (get_monitor_config); @@ -83,6 +85,8 @@ public class Display.MonitorManager : GLib.Object { critical (e.message); } + var monitor_number = virtual_monitors.size; + // Clear all monitors and virtual monitors before re-adding them if needed monitors.clear (); virtual_monitors.clear (); @@ -239,6 +243,16 @@ public class Display.MonitorManager : GLib.Object { virtual_monitor.scale = virtual_monitors[0].scale; add_virtual_monitor (virtual_monitor); } + + + } + + if (monitor_number != virtual_monitors.size) { + notify_property ("virtual-monitor-number"); + } + + if (virtual_monitors.size >= 2) { + layout_manager.arrange_monitors (virtual_monitors); } } @@ -402,6 +416,8 @@ public class Display.MonitorManager : GLib.Object { virtual_monitors.clear (); virtual_monitors.add_all (new_virtual_monitors); + layout_manager.arrange_monitors (virtual_monitors); + notify_property ("virtual-monitor-number"); notify_property ("is-mirrored"); } From b2c0ff18210ff07e041cc844d6ae9812a990d92f Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Fri, 30 May 2025 20:27:00 -0300 Subject: [PATCH 04/19] Fix build --- src/Objects/MonitorLayoutManager.vala | 4 ++-- src/Objects/MonitorLayoutProfile.vala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index a701a7f7..93834265 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -2,7 +2,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later * SPDX-FileCopyrightText: 2025 elementary, Inc. * - * Authored by: Leonardo Lemos + * Authored by: Leonardo Lemos */ public class Display.MonitorLayoutManager : GLib.Object { @@ -128,7 +128,7 @@ public class Display.MonitorLayoutManager : GLib.Object { props_builder.add_value (new Variant.dict_entry ("x", new Variant.variant(new Variant.int32 (monitor.x)))); props_builder.add_value (new Variant.dict_entry ("y", new Variant.variant(new Variant.int32 (monitor.y)))); - dict_builder.add_value (new Variant.dict_entry (key, props_builder)); + dict_builder.add_value (new Variant.dict_entry (key, props_builder.end ())); } return dict_builder.end (); diff --git a/src/Objects/MonitorLayoutProfile.vala b/src/Objects/MonitorLayoutProfile.vala index 543f0f8b..7791ac22 100644 --- a/src/Objects/MonitorLayoutProfile.vala +++ b/src/Objects/MonitorLayoutProfile.vala @@ -2,7 +2,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later * SPDX-FileCopyrightText: 2025 elementary, Inc. * - * Authored by: Leonardo Lemos + * Authored by: Leonardo Lemos */ public class Display.MonitorLayoutProfile : GLib.Object { From 441b2800fcb9bad09eeba90c8f9d02161c6d217e Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Sat, 31 May 2025 18:28:45 -0300 Subject: [PATCH 05/19] Notify monitor number change only once --- src/Objects/MonitorManager.vala | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index 98f9e21d..1708e980 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -221,7 +221,7 @@ public class Display.MonitorManager : GLib.Object { virtual_monitor.scale = mutter_logical_monitor.scale; virtual_monitor.transform = mutter_logical_monitor.transform; virtual_monitor.primary = mutter_logical_monitor.primary; - add_virtual_monitor (virtual_monitor); + virtual_monitors.add (virtual_monitor); } // Look for any monitors that aren't part of a virtual monitor (hence disabled) @@ -241,19 +241,15 @@ public class Display.MonitorManager : GLib.Object { virtual_monitor.primary = false; virtual_monitor.monitors.add (monitor); virtual_monitor.scale = virtual_monitors[0].scale; - add_virtual_monitor (virtual_monitor); + virtual_monitors.add (virtual_monitor); } - - } if (monitor_number != virtual_monitors.size) { notify_property ("virtual-monitor-number"); } - if (virtual_monitors.size >= 2) { - layout_manager.arrange_monitors (virtual_monitors); - } + layout_manager.arrange_monitors (virtual_monitors); } public void set_monitor_config () throws Error { @@ -422,11 +418,6 @@ public class Display.MonitorManager : GLib.Object { notify_property ("is-mirrored"); } - private void add_virtual_monitor (Display.VirtualMonitor virtual_monitor) { - virtual_monitors.add (virtual_monitor); - notify_property ("virtual-monitor-number"); - } - private VirtualMonitor? get_virtual_monitor_by_id (string id) { foreach (var vm in virtual_monitors) { if (vm.id == id) { From 12a1a02dba3365eb968f19a42d99190f6408cf85 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Sat, 31 May 2025 18:29:04 -0300 Subject: [PATCH 06/19] Show display label only once --- src/Widgets/DisplaysOverlay.vala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Widgets/DisplaysOverlay.vala b/src/Widgets/DisplaysOverlay.vala index d76bb41c..b429c468 100644 --- a/src/Widgets/DisplaysOverlay.vala +++ b/src/Widgets/DisplaysOverlay.vala @@ -199,6 +199,7 @@ public class Display.DisplaysOverlay : Gtk.Box { add_output (virtual_monitor); } + show_windows (); change_active_displays_sensitivity (); calculate_ratio (); scanning = false; @@ -351,10 +352,6 @@ public class Display.DisplaysOverlay : Gtk.Box { check_configuration_change (); calculate_ratio (); }); - - if (!monitor_manager.is_mirrored && virtual_monitor.is_active) { - show_windows (); - } } private void set_as_primary (Display.VirtualMonitor new_primary) { From f8605150ac38418143789b08041eb3708094fe34 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Sat, 31 May 2025 19:18:13 -0300 Subject: [PATCH 07/19] Rescan Displays only if monitors changed --- src/Objects/MonitorManager.vala | 21 ++++++++++++++------- src/Widgets/DisplaysOverlay.vala | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index 1708e980..7947f1bd 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -46,6 +46,8 @@ public class Display.MonitorManager : GLib.Object { } } + public signal void monitors_changed (); + private MutterDisplayConfigInterface iface; private uint current_serial; private MonitorLayoutManager layout_manager; @@ -68,8 +70,14 @@ public class Display.MonitorManager : GLib.Object { virtual_monitors = new Gee.LinkedList (); layout_manager = new MonitorLayoutManager (); try { - iface = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.Mutter.DisplayConfig", "/org/gnome/Mutter/DisplayConfig"); - iface.monitors_changed.connect (get_monitor_config); + iface = Bus.get_proxy_sync ( + BusType.SESSION, + "org.gnome.Mutter.DisplayConfig", + "/org/gnome/Mutter/DisplayConfig"); + iface.monitors_changed.connect (() => { + get_monitor_config (); + monitors_changed (); + }); } catch (Error e) { critical (e.message); } @@ -80,7 +88,10 @@ public class Display.MonitorManager : GLib.Object { MutterReadLogicalMonitor[] mutter_logical_monitors; GLib.HashTable properties; try { - iface.get_current_state (out current_serial, out mutter_monitors, out mutter_logical_monitors, out properties); + iface.get_current_state (out current_serial, + out mutter_monitors, + out mutter_logical_monitors, + out properties); } catch (Error e) { critical (e.message); } @@ -245,10 +256,6 @@ public class Display.MonitorManager : GLib.Object { } } - if (monitor_number != virtual_monitors.size) { - notify_property ("virtual-monitor-number"); - } - layout_manager.arrange_monitors (virtual_monitors); } diff --git a/src/Widgets/DisplaysOverlay.vala b/src/Widgets/DisplaysOverlay.vala index b429c468..f1657964 100644 --- a/src/Widgets/DisplaysOverlay.vala +++ b/src/Widgets/DisplaysOverlay.vala @@ -87,7 +87,7 @@ public class Display.DisplaysOverlay : Gtk.Box { add_controller (drag_gesture); monitor_manager = Display.MonitorManager.get_default (); - monitor_manager.notify["virtual-monitor-number"].connect (() => rescan_displays ()); + monitor_manager.monitors_changed.connect (() => rescan_displays ()); rescan_displays (); overlay.get_child_position.connect (get_child_position); From 671246144b7be3c346f41ad98e7d55e80d95b15c Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Sat, 31 May 2025 19:18:40 -0300 Subject: [PATCH 08/19] Add display transformation saving --- src/Objects/MonitorLayoutManager.vala | 86 +++++++++++++++++---------- src/Objects/MonitorLayoutProfile.vala | 11 ++-- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index 93834265..547c986b 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -9,7 +9,7 @@ public class Display.MonitorLayoutManager : GLib.Object { private Settings settings; private const string PREFERRED_MONITOR_LAYOUTS_KEY = "preferred-display-layouts"; - + public MonitorLayoutManager () { Object (); } @@ -23,25 +23,29 @@ public class Display.MonitorLayoutManager : GLib.Object { // If there's only one monitor, no need to arrange return; } - + var layout_key = get_layout_key (virtual_monitors); var layout = find_match_layout (layout_key); var is_cloned = is_virtual_monitors_cloned (virtual_monitors); var has_update = false; - if (layout != null) { foreach (var virtual_monitor in virtual_monitors) { - var monitor_layout = layout.find_position_by_id (virtual_monitor.monitors[0].hash.to_string ()); - - if (monitor_layout != null) { - if ((virtual_monitor.x != monitor_layout.x || virtual_monitor.y != monitor_layout.y) && !is_cloned) { + var monitor_position = layout + .find_position_by_id (virtual_monitor.monitors[0].hash.to_string ()); + + if (monitor_position != null) { + if ((virtual_monitor.x != monitor_position.x + || virtual_monitor.y != monitor_position.y + || virtual_monitor.transform != monitor_position.transform) + && !is_cloned) { has_update = true; break; } - virtual_monitor.x = monitor_layout.x; - virtual_monitor.y = monitor_layout.y; + virtual_monitor.x = monitor_position.x; + virtual_monitor.y = monitor_position.y; + virtual_monitor.transform = monitor_position.transform; } } } else { @@ -50,6 +54,7 @@ public class Display.MonitorLayoutManager : GLib.Object { } if (has_update) { + // If the layout has been updated, save the new layout save_layout (virtual_monitors); } } @@ -61,11 +66,10 @@ public class Display.MonitorLayoutManager : GLib.Object { var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); add_or_update_layout (layouts, key, layout_variant); - } public MonitorLayoutProfile? find_match_layout (string key) { - // Layouts format is 'a{sa{sv}}' + // Layouts format are 'a{sa{sa{sv}}}' var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); if (layouts == null || layouts.n_children () == 0) { @@ -81,25 +85,32 @@ public class Display.MonitorLayoutManager : GLib.Object { continue; } - var virtual_monitor_layout = new MonitorLayoutProfile (layout_key); + var virtual_monitor_position = new MonitorLayoutProfile (layout_key); // Process the monitors in the layout for (var j = 0; j < monitors.n_children (); j++) { var monitor = monitors.get_child_value (j); var monitor_props = monitor.get_child_value (1); - warning (monitor_props.get_child_value (0).get_type ().dup_string ()); - warning (monitor_props.get_child_value (0).get_child_value (1).get_child_value (0).get_type ().dup_string ()); - - - virtual_monitor_layout.add_position ( - monitor.get_child_value (0).get_string (), // id - monitor_props.get_child_value (0).get_child_value (1).get_child_value (0).get_int32 (), // x position - monitor_props.get_child_value (1).get_child_value (1).get_child_value (0).get_int32 () // y position - ); + virtual_monitor_position.add_position ( + monitor.get_child_value (0) + .get_string (), // id + monitor_props.get_child_value (0) + .get_child_value (1) + .get_child_value (0) + .get_int32 (), // x position + monitor_props.get_child_value (1) + .get_child_value (1) + .get_child_value (0) + .get_int32 (), // y position + monitor_props.get_child_value (2) + .get_child_value (1) + .get_child_value (0) + .get_int32 () // transform + ); } - return virtual_monitor_layout; + return virtual_monitor_position; } return null; @@ -119,23 +130,32 @@ public class Display.MonitorLayoutManager : GLib.Object { } private GLib.Variant build_layout_variant (Gee.LinkedList virtual_monitors) { - var dict_builder = new VariantBuilder(VariantType.DICTIONARY); + var dict_builder = new VariantBuilder (VariantType.DICTIONARY); foreach (var monitor in virtual_monitors) { - var props_builder = new VariantBuilder(VariantType.DICTIONARY); - var key = monitor.monitors.get(0).hash.to_string(); - - props_builder.add_value (new Variant.dict_entry ("x", new Variant.variant(new Variant.int32 (monitor.x)))); - props_builder.add_value (new Variant.dict_entry ("y", new Variant.variant(new Variant.int32 (monitor.y)))); - - dict_builder.add_value (new Variant.dict_entry (key, props_builder.end ())); + var props_builder = new VariantBuilder (VariantType.DICTIONARY); + var key = monitor.monitors.get (0).hash.to_string (); + + var coordinate_x_variant = new Variant.variant (new Variant.int32 (monitor.x)); + var coordinate_y_variant = new Variant.variant (new Variant.int32 (monitor.y)); + var transform_variant = new Variant.variant (new Variant.int32 (monitor.transform)); + + props_builder.add_value (new Variant.dict_entry ("x", coordinate_x_variant)); + props_builder.add_value (new Variant.dict_entry ("y", coordinate_y_variant)); + props_builder.add_value (new Variant.dict_entry ("transform", transform_variant)); + + var props_variant = props_builder.end (); + + warning (props_variant.print (true)); + + dict_builder.add_value (new Variant.dict_entry (key, props_variant)); } return dict_builder.end (); } private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { - var layout_builder = new VariantBuilder(VariantType.DICTIONARY); + var layout_builder = new VariantBuilder (VariantType.DICTIONARY); bool found = false; for (var i = 0; i < layouts.n_children (); i++) { @@ -157,7 +177,7 @@ public class Display.MonitorLayoutManager : GLib.Object { layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); } - settings.set_value(PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); + settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); } private bool is_virtual_monitors_cloned (Gee.LinkedList virtual_monitors) { @@ -169,4 +189,4 @@ public class Display.MonitorLayoutManager : GLib.Object { return true; } -} \ No newline at end of file +} diff --git a/src/Objects/MonitorLayoutProfile.vala b/src/Objects/MonitorLayoutProfile.vala index 7791ac22..1c8822c4 100644 --- a/src/Objects/MonitorLayoutProfile.vala +++ b/src/Objects/MonitorLayoutProfile.vala @@ -10,9 +10,10 @@ public class Display.MonitorLayoutProfile : GLib.Object { public string id { get; set; } public int x { get; set; } public int y { get; set; } + public DisplayTransform transform { get; set; } - public MonitorPosition (string id, int x, int y) { - Object (id: id, x: x, y: y); + public MonitorPosition (string id, int x, int y, DisplayTransform transform) { + Object (id: id, x: x, y: y, transform: transform); } } @@ -33,8 +34,8 @@ public class Display.MonitorLayoutProfile : GLib.Object { _positions = new List (); } - public void add_position (string id, int x, int y) { - _positions.append (new MonitorPosition (id, x, y)); + public void add_position (string id, int x, int y, DisplayTransform transform) { + _positions.append (new MonitorPosition (id, x, y, transform)); } public MonitorPosition? find_position_by_id (string id) { @@ -45,4 +46,4 @@ public class Display.MonitorLayoutProfile : GLib.Object { } return null; } -} \ No newline at end of file +} From 756c165047d587d79e1a05f12f9642dc86872820 Mon Sep 17 00:00:00 2001 From: Leonardo Lemos Date: Sun, 1 Jun 2025 19:11:36 -0300 Subject: [PATCH 09/19] Removed unused variable --- src/Objects/MonitorManager.vala | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index 7947f1bd..de91eac5 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -96,8 +96,6 @@ public class Display.MonitorManager : GLib.Object { critical (e.message); } - var monitor_number = virtual_monitors.size; - // Clear all monitors and virtual monitors before re-adding them if needed monitors.clear (); virtual_monitors.clear (); From 3690b20a4c10774c387eb5bd39bacc6e707ccfea Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 11:05:21 +0100 Subject: [PATCH 10/19] Drop unnecessary check --- src/Objects/MonitorLayoutManager.vala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index 547c986b..8243d93e 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -21,24 +21,26 @@ public class Display.MonitorLayoutManager : GLib.Object { public void arrange_monitors (Gee.LinkedList virtual_monitors) { if (virtual_monitors.size == 1) { // If there's only one monitor, no need to arrange + // Cloned monitors only have one virtual monitor so will return here return; } var layout_key = get_layout_key (virtual_monitors); var layout = find_match_layout (layout_key); - var is_cloned = is_virtual_monitors_cloned (virtual_monitors); + // var is_cloned = is_virtual_monitors_cloned (virtual_monitors); var has_update = false; if (layout != null) { foreach (var virtual_monitor in virtual_monitors) { - var monitor_position = layout - .find_position_by_id (virtual_monitor.monitors[0].hash.to_string ()); + var monitor_position = layout.find_position_by_id ( + virtual_monitor.monitors[0].hash.to_string () + ); if (monitor_position != null) { if ((virtual_monitor.x != monitor_position.x || virtual_monitor.y != monitor_position.y - || virtual_monitor.transform != monitor_position.transform) - && !is_cloned) { + || virtual_monitor.transform != monitor_position.transform)) { + has_update = true; break; } From e43a21c2580393b6c88b04bde5ec8b108890a9f9 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 11:12:28 +0100 Subject: [PATCH 11/19] Reduce scope of some functions --- src/Objects/MonitorLayoutManager.vala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index 8243d93e..ff93c783 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -27,7 +27,6 @@ public class Display.MonitorLayoutManager : GLib.Object { var layout_key = get_layout_key (virtual_monitors); var layout = find_match_layout (layout_key); - // var is_cloned = is_virtual_monitors_cloned (virtual_monitors); var has_update = false; if (layout != null) { @@ -61,7 +60,7 @@ public class Display.MonitorLayoutManager : GLib.Object { } } - public void save_layout (Gee.LinkedList virtual_monitors) { + private void save_layout (Gee.LinkedList virtual_monitors) { var key = get_layout_key (virtual_monitors); var layout_variant = build_layout_variant (virtual_monitors); @@ -70,7 +69,7 @@ public class Display.MonitorLayoutManager : GLib.Object { add_or_update_layout (layouts, key, layout_variant); } - public MonitorLayoutProfile? find_match_layout (string key) { + private MonitorLayoutProfile? find_match_layout (string key) { // Layouts format are 'a{sa{sa{sv}}}' var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); From a9f5d711c58c3473d937bb565bf067f0c1ebb4f8 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 11:56:56 +0100 Subject: [PATCH 12/19] Simplify arrange_monitors() & get_layout_key() --- src/Objects/MonitorLayoutManager.vala | 71 ++++----------------------- 1 file changed, 10 insertions(+), 61 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index ff93c783..60b07eb0 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -27,37 +27,21 @@ public class Display.MonitorLayoutManager : GLib.Object { var layout_key = get_layout_key (virtual_monitors); var layout = find_match_layout (layout_key); - var has_update = false; if (layout != null) { foreach (var virtual_monitor in virtual_monitors) { - var monitor_position = layout.find_position_by_id ( - virtual_monitor.monitors[0].hash.to_string () - ); - - if (monitor_position != null) { - if ((virtual_monitor.x != monitor_position.x - || virtual_monitor.y != monitor_position.y - || virtual_monitor.transform != monitor_position.transform)) { - - has_update = true; - break; - } - - virtual_monitor.x = monitor_position.x; - virtual_monitor.y = monitor_position.y; - virtual_monitor.transform = monitor_position.transform; + DisplayTransform transform; + int x, y; + if (layout.lookup (virtual_monitor.id, "(iiu)", out x, out y, out transform)) { + virtual_monitor.x = x; + virtual_monitor.y = y; + virtual_monitor.transform = transform; } } } else { // If no layout found, we save the current layout to use later save_layout (virtual_monitors); } - - if (has_update) { - // If the layout has been updated, save the new layout - save_layout (virtual_monitors); - } } private void save_layout (Gee.LinkedList virtual_monitors) { @@ -69,7 +53,7 @@ public class Display.MonitorLayoutManager : GLib.Object { add_or_update_layout (layouts, key, layout_variant); } - private MonitorLayoutProfile? find_match_layout (string key) { + private VariantDict? find_match_layout (string key) { // Layouts format are 'a{sa{sa{sv}}}' var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); @@ -77,44 +61,9 @@ public class Display.MonitorLayoutManager : GLib.Object { return null; // No layouts saved } - for (var i = 0; i < layouts.n_children (); i++) { - var layout = layouts.get_child_value (i); - var layout_key = layout.get_child_value (0).get_string (); - var monitors = layout.get_child_value (1); - - if (layout_key != key) { - continue; - } - - var virtual_monitor_position = new MonitorLayoutProfile (layout_key); - - // Process the monitors in the layout - for (var j = 0; j < monitors.n_children (); j++) { - var monitor = monitors.get_child_value (j); - var monitor_props = monitor.get_child_value (1); - - virtual_monitor_position.add_position ( - monitor.get_child_value (0) - .get_string (), // id - monitor_props.get_child_value (0) - .get_child_value (1) - .get_child_value (0) - .get_int32 (), // x position - monitor_props.get_child_value (1) - .get_child_value (1) - .get_child_value (0) - .get_int32 (), // y position - monitor_props.get_child_value (2) - .get_child_value (1) - .get_child_value (0) - .get_int32 () // transform - ); - } - - return virtual_monitor_position; - } - - return null; + VariantDict? monitors = null; + layouts.lookup (key, "a{sa{sv}}", out monitors); + return monitors; } private string get_layout_key (Gee.LinkedList virtual_monitors) { From c5071c62e0258feea88a0e6bbfe2eff9ca51cc56 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 11:59:33 +0100 Subject: [PATCH 13/19] Lose unused object --- src/Objects/MonitorLayoutProfile.vala | 49 --------------------------- src/meson.build | 1 - 2 files changed, 50 deletions(-) delete mode 100644 src/Objects/MonitorLayoutProfile.vala diff --git a/src/Objects/MonitorLayoutProfile.vala b/src/Objects/MonitorLayoutProfile.vala deleted file mode 100644 index 1c8822c4..00000000 --- a/src/Objects/MonitorLayoutProfile.vala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-2.0-or-later - * SPDX-FileCopyrightText: 2025 elementary, Inc. - * - * Authored by: Leonardo Lemos - */ - -public class Display.MonitorLayoutProfile : GLib.Object { - public class MonitorPosition : GLib.Object { - public string id { get; set; } - public int x { get; set; } - public int y { get; set; } - public DisplayTransform transform { get; set; } - - public MonitorPosition (string id, int x, int y, DisplayTransform transform) { - Object (id: id, x: x, y: y, transform: transform); - } - } - - public string id { get; set; } - private List _positions; - - public unowned List positions { - get { - return _positions; - } - } - - public MonitorLayoutProfile (string id) { - Object (id: id); - } - - construct { - _positions = new List (); - } - - public void add_position (string id, int x, int y, DisplayTransform transform) { - _positions.append (new MonitorPosition (id, x, y, transform)); - } - - public MonitorPosition? find_position_by_id (string id) { - foreach (var position in _positions) { - if (position.id == id) { - return position; - } - } - return null; - } -} diff --git a/src/meson.build b/src/meson.build index 8ed4bba3..6ed4f693 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,7 +11,6 @@ plug_files = files( 'Objects/MonitorManager.vala', 'Objects/Monitor.vala', 'Objects/MonitorLayoutManager.vala', - 'Objects/MonitorLayoutProfile.vala', 'Views/NightLightView.vala', 'Views/DisplaysView.vala', 'Views' / 'FiltersView.vala', From 0e85221e618a0d0de1f1bd92df5078b6317f9625 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 12:12:58 +0100 Subject: [PATCH 14/19] Inline simplified find_match_layout() --- src/Objects/MonitorLayoutManager.vala | 31 +++++++++++---------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index 60b07eb0..bcafc6f3 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -26,22 +26,28 @@ public class Display.MonitorLayoutManager : GLib.Object { } var layout_key = get_layout_key (virtual_monitors); - var layout = find_match_layout (layout_key); + // Layouts format are 'a{sa{sa{sv}}}' + var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); + VariantDict? monitors = null; + if (layouts == null && + layouts.lookup (layout_key, "a{sa{sv}}", out monitors) && + monitors != null) { - if (layout != null) { foreach (var virtual_monitor in virtual_monitors) { DisplayTransform transform; int x, y; - if (layout.lookup (virtual_monitor.id, "(iiu)", out x, out y, out transform)) { + if (monitors.lookup (virtual_monitor.id, "(iiu)", out x, out y, out transform)) { virtual_monitor.x = x; virtual_monitor.y = y; virtual_monitor.transform = transform; } } - } else { - // If no layout found, we save the current layout to use later - save_layout (virtual_monitors); + + return; } + + // If no layout found, we save the current layout to use later + save_layout (virtual_monitors); } private void save_layout (Gee.LinkedList virtual_monitors) { @@ -53,19 +59,6 @@ public class Display.MonitorLayoutManager : GLib.Object { add_or_update_layout (layouts, key, layout_variant); } - private VariantDict? find_match_layout (string key) { - // Layouts format are 'a{sa{sa{sv}}}' - var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); - - if (layouts == null || layouts.n_children () == 0) { - return null; // No layouts saved - } - - VariantDict? monitors = null; - layouts.lookup (key, "a{sa{sv}}", out monitors); - return monitors; - } - private string get_layout_key (Gee.LinkedList virtual_monitors) { // Generate a unique key based on the virtual monitors' monitors hashes var key = new StringBuilder (); From a90c35983a20bc37725f01665b563084d93f6288 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 12:22:15 +0100 Subject: [PATCH 15/19] Drop hash for key --- src/Objects/MonitorLayoutManager.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index bcafc6f3..f4e92d42 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -65,11 +65,11 @@ public class Display.MonitorLayoutManager : GLib.Object { foreach (var virtual_monitor in virtual_monitors) { foreach (var monitor in virtual_monitor.monitors) { - key.append (monitor.hash.to_string ()); + key.append (virtual_monitor.id); } } - return key.str.hash ().to_string (); + return key.str; } private GLib.Variant build_layout_variant (Gee.LinkedList virtual_monitors) { From 4442fa32ecbf57a6797edf34b90420da3c7b6293 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 12:37:05 +0100 Subject: [PATCH 16/19] Inline build_layout_variant() --- src/Objects/MonitorLayoutManager.vala | 45 +++++++++++++-------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index f4e92d42..f6d67b0d 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -51,33 +51,13 @@ public class Display.MonitorLayoutManager : GLib.Object { } private void save_layout (Gee.LinkedList virtual_monitors) { - var key = get_layout_key (virtual_monitors); - var layout_variant = build_layout_variant (virtual_monitors); - var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); - - add_or_update_layout (layouts, key, layout_variant); - } - - private string get_layout_key (Gee.LinkedList virtual_monitors) { - // Generate a unique key based on the virtual monitors' monitors hashes - var key = new StringBuilder (); - - foreach (var virtual_monitor in virtual_monitors) { - foreach (var monitor in virtual_monitor.monitors) { - key.append (virtual_monitor.id); - } - } - - return key.str; - } - private GLib.Variant build_layout_variant (Gee.LinkedList virtual_monitors) { + //Build the layout variant var dict_builder = new VariantBuilder (VariantType.DICTIONARY); - foreach (var monitor in virtual_monitors) { var props_builder = new VariantBuilder (VariantType.DICTIONARY); - var key = monitor.monitors.get (0).hash.to_string (); + var monitor_key = monitor.monitors.get (0).hash.to_string (); var coordinate_x_variant = new Variant.variant (new Variant.int32 (monitor.x)); var coordinate_y_variant = new Variant.variant (new Variant.int32 (monitor.y)); @@ -91,10 +71,27 @@ public class Display.MonitorLayoutManager : GLib.Object { warning (props_variant.print (true)); - dict_builder.add_value (new Variant.dict_entry (key, props_variant)); + dict_builder.add_value (new Variant.dict_entry (monitor_key, props_variant)); + } + + var layout_variant = dict_builder.end (); + // Add or update the layouts setting + var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); + var layout_key = get_layout_key (virtual_monitors); + add_or_update_layout (layouts, layout_key, layout_variant); + } + + private string get_layout_key (Gee.LinkedList virtual_monitors) { + // Generate a unique key based on the virtual monitors' monitors hashes + var key = new StringBuilder (); + + foreach (var virtual_monitor in virtual_monitors) { + foreach (var monitor in virtual_monitor.monitors) { + key.append (virtual_monitor.id); + } } - return dict_builder.end (); + return key.str; } private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { From 844dc0453585dcffe90e968b1b4b0a99e8e4f36b Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 12:51:35 +0100 Subject: [PATCH 17/19] Inline add_or_update_layout() --- src/Objects/MonitorLayoutManager.vala | 76 +++++++++++++++++---------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index f6d67b0d..51137c77 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -51,8 +51,6 @@ public class Display.MonitorLayoutManager : GLib.Object { } private void save_layout (Gee.LinkedList virtual_monitors) { - - //Build the layout variant var dict_builder = new VariantBuilder (VariantType.DICTIONARY); foreach (var monitor in virtual_monitors) { @@ -75,51 +73,73 @@ public class Display.MonitorLayoutManager : GLib.Object { } var layout_variant = dict_builder.end (); - // Add or update the layouts setting - var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); - var layout_key = get_layout_key (virtual_monitors); - add_or_update_layout (layouts, layout_key, layout_variant); - } - - private string get_layout_key (Gee.LinkedList virtual_monitors) { - // Generate a unique key based on the virtual monitors' monitors hashes - var key = new StringBuilder (); - - foreach (var virtual_monitor in virtual_monitors) { - foreach (var monitor in virtual_monitor.monitors) { - key.append (virtual_monitor.id); - } - } - - return key.str; - } - private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { - var layout_builder = new VariantBuilder (VariantType.DICTIONARY); + // Add or update the layouts setting + var save_key = get_layout_key (virtual_monitors); + dict_builder = new VariantBuilder (VariantType.DICTIONARY); bool found = false; - + var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); for (var i = 0; i < layouts.n_children (); i++) { var layout = layouts.get_child_value (i); var layout_key = layout.get_child_value (0).get_string (); - if (layout_key == key) { + if (layout_key == save_key) { // Update existing layout - layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + dict_builder.add_value (new Variant.dict_entry (save_key, layout_variant)); found = true; } else { // Keep existing layout - layout_builder.add_value (new Variant.dict_entry (layout_key, layout)); + dict_builder.add_value (new Variant.dict_entry (layout_key, layout)); } } if (!found) { // Add new layout - layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + dict_builder.add_value (new Variant.dict_entry (save_key, layout_variant)); + } + + settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, dict_builder.end ()); + } + + private string get_layout_key (Gee.LinkedList virtual_monitors) { + // Generate a unique key based on the virtual monitors' monitors hashes + var key = new StringBuilder (); + + foreach (var virtual_monitor in virtual_monitors) { + foreach (var monitor in virtual_monitor.monitors) { + key.append (virtual_monitor.id); + } } - settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); + return key.str; } + // private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { + // var layout_builder = new VariantBuilder (VariantType.DICTIONARY); + // bool found = false; + + // for (var i = 0; i < layouts.n_children (); i++) { + // var layout = layouts.get_child_value (i); + // var layout_key = layout.get_child_value (0).get_string (); + + // if (layout_key == key) { + // // Update existing layout + // layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + // found = true; + // } else { + // // Keep existing layout + // layout_builder.add_value (new Variant.dict_entry (layout_key, layout)); + // } + // } + + // if (!found) { + // // Add new layout + // layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); + // } + + // settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); + // } + private bool is_virtual_monitors_cloned (Gee.LinkedList virtual_monitors) { foreach (var monitor in virtual_monitors) { if (monitor.x != 0 || monitor.y != 0) { From 11ebe2cc1354cd515c45cd60909814e4b1bb2ad6 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 13:18:08 +0100 Subject: [PATCH 18/19] Simplify save layout using VariantDict --- src/Objects/MonitorLayoutManager.vala | 73 ++++----------------------- 1 file changed, 11 insertions(+), 62 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index 51137c77..efa3831b 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -52,52 +52,27 @@ public class Display.MonitorLayoutManager : GLib.Object { private void save_layout (Gee.LinkedList virtual_monitors) { //Build the layout variant - var dict_builder = new VariantBuilder (VariantType.DICTIONARY); + var dict_builder = new VariantDict (); foreach (var monitor in virtual_monitors) { - var props_builder = new VariantBuilder (VariantType.DICTIONARY); - var monitor_key = monitor.monitors.get (0).hash.to_string (); - - var coordinate_x_variant = new Variant.variant (new Variant.int32 (monitor.x)); - var coordinate_y_variant = new Variant.variant (new Variant.int32 (monitor.y)); - var transform_variant = new Variant.variant (new Variant.int32 (monitor.transform)); - - props_builder.add_value (new Variant.dict_entry ("x", coordinate_x_variant)); - props_builder.add_value (new Variant.dict_entry ("y", coordinate_y_variant)); - props_builder.add_value (new Variant.dict_entry ("transform", transform_variant)); - + var props_builder = new VariantDict (); + // We save three properties for now, may want to save more later + props_builder.insert ("x", "v", new Variant.int32 (monitor.x)); + props_builder.insert ("y", "v", new Variant.int32 (monitor.y)); + props_builder.insert ("transform", "v", new Variant.uint32 (monitor.transform)); var props_variant = props_builder.end (); - - warning (props_variant.print (true)); - - dict_builder.add_value (new Variant.dict_entry (monitor_key, props_variant)); + debug (props_variant.print (true)); + dict_builder.insert_value (monitor.id, props_variant); } var layout_variant = dict_builder.end (); // Add or update the layouts setting var save_key = get_layout_key (virtual_monitors); - dict_builder = new VariantBuilder (VariantType.DICTIONARY); - bool found = false; var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); - for (var i = 0; i < layouts.n_children (); i++) { - var layout = layouts.get_child_value (i); - var layout_key = layout.get_child_value (0).get_string (); - - if (layout_key == save_key) { - // Update existing layout - dict_builder.add_value (new Variant.dict_entry (save_key, layout_variant)); - found = true; - } else { - // Keep existing layout - dict_builder.add_value (new Variant.dict_entry (layout_key, layout)); - } - } - - if (!found) { - // Add new layout - dict_builder.add_value (new Variant.dict_entry (save_key, layout_variant)); - } + dict_builder = new VariantDict (layouts); + dict_builder.insert_value (save_key, layout_variant); + // Save to settings settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, dict_builder.end ()); } @@ -114,32 +89,6 @@ public class Display.MonitorLayoutManager : GLib.Object { return key.str; } - // private void add_or_update_layout (GLib.Variant layouts, string key, GLib.Variant layout_variant) { - // var layout_builder = new VariantBuilder (VariantType.DICTIONARY); - // bool found = false; - - // for (var i = 0; i < layouts.n_children (); i++) { - // var layout = layouts.get_child_value (i); - // var layout_key = layout.get_child_value (0).get_string (); - - // if (layout_key == key) { - // // Update existing layout - // layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); - // found = true; - // } else { - // // Keep existing layout - // layout_builder.add_value (new Variant.dict_entry (layout_key, layout)); - // } - // } - - // if (!found) { - // // Add new layout - // layout_builder.add_value (new Variant.dict_entry (key, layout_variant)); - // } - - // settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layout_builder.end ()); - // } - private bool is_virtual_monitors_cloned (Gee.LinkedList virtual_monitors) { foreach (var monitor in virtual_monitors) { if (monitor.x != 0 || monitor.y != 0) { From 5ee9993cacaf75fe46d321dcff954f3b465da7f0 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 27 May 2026 13:27:49 +0100 Subject: [PATCH 19/19] Do not arrange unnecessarily in get_monitor_config() --- src/Objects/MonitorLayoutManager.vala | 2 +- src/Objects/MonitorManager.vala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Objects/MonitorLayoutManager.vala b/src/Objects/MonitorLayoutManager.vala index efa3831b..c3ad2c85 100644 --- a/src/Objects/MonitorLayoutManager.vala +++ b/src/Objects/MonitorLayoutManager.vala @@ -50,7 +50,7 @@ public class Display.MonitorLayoutManager : GLib.Object { save_layout (virtual_monitors); } - private void save_layout (Gee.LinkedList virtual_monitors) { + public void save_layout (Gee.LinkedList virtual_monitors) { //Build the layout variant var dict_builder = new VariantDict (); foreach (var monitor in virtual_monitors) { diff --git a/src/Objects/MonitorManager.vala b/src/Objects/MonitorManager.vala index de91eac5..0c8f7ad1 100644 --- a/src/Objects/MonitorManager.vala +++ b/src/Objects/MonitorManager.vala @@ -254,7 +254,9 @@ public class Display.MonitorManager : GLib.Object { } } - layout_manager.arrange_monitors (virtual_monitors); + if (!is_mirrored) { + layout_manager.save_layout (virtual_monitors); + } } public void set_monitor_config () throws Error {