-
-
Notifications
You must be signed in to change notification settings - Fork 19
Introduce Monitor Layout Manager #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
leonardo-lemos
wants to merge
34
commits into
main
Choose a base branch
from
introduce-layout-manager
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
1ea364c
Add gschema
leonardo-lemos 762da1f
Implement MonitorLayoutManager
leonardo-lemos dd6c098
Use MonitorLayoutManager
leonardo-lemos b2c0ff1
Fix build
leonardo-lemos 36dd686
Merge branch 'main' into introduce-layout-manager
leonardo-lemos 441b280
Notify monitor number change only once
leonardo-lemos 12a1a02
Show display label only once
leonardo-lemos f860515
Rescan Displays only if monitors changed
leonardo-lemos 6712461
Add display transformation saving
leonardo-lemos 756c165
Removed unused variable
leonardo-lemos 26bcac8
Merge branch 'main' into introduce-layout-manager
zeebok 756431b
Merge branch 'main' into introduce-layout-manager
jeremypw 3690b20
Drop unnecessary check
jeremypw e43a21c
Reduce scope of some functions
jeremypw a9f5d71
Simplify arrange_monitors() & get_layout_key()
jeremypw c5071c6
Lose unused object
jeremypw 0e85221
Inline simplified find_match_layout()
jeremypw a90c359
Drop hash for key
jeremypw 4442fa3
Inline build_layout_variant()
jeremypw 844dc04
Inline add_or_update_layout()
jeremypw 11ebe2c
Simplify save layout using VariantDict
jeremypw 5ee9993
Do not arrange unnecessarily in get_monitor_config()
jeremypw 1b7cdc9
Merge branch 'main' into introduce-layout-manager
jeremypw c07e6cb
Amend schema variant type to match that generated by simplified save …
jeremypw 2b27cd6
Add comment and expanded schema description
jeremypw 4d4b5b7
Fix saving and restoring layouts
jeremypw 7c7b111
Save and restore which monitor is primary
jeremypw f8633d0
Remove extraneous code
jeremypw c741f5d
Remove unused
jeremypw e0f0912
Arrange monitors after getting config to fix toggling mirror mode
jeremypw 6aaaf57
Rescan displays on monitors-changed signal to redraw after "Keep prev…
jeremypw a76d5b9
Save and restore is-active property
jeremypw fb242bc
Mention "is-active" property in schema description
jeremypw 2af8f91
Update some comments
jeremypw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <schemalist> | ||
| <schema id="io.elementary.settings.display" path="/io/elementary/settings/display/"> | ||
|
|
||
| <key name="preferred-display-layouts" type="a{sv}"> | ||
| <default>{}</default> | ||
| <summary>Preferred display layouts</summary> | ||
| <description> | ||
| 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. | ||
| The expanded variant type is "a{sa{sa{sv}}}" | ||
| Setting: Dictionary of layouts - key is based on a combination of monitor ids | ||
| Layout: Dictionary of monitors - key is monitor identifier | ||
| Monitor: Dictionary of property values - key is property name | ||
| Property keys: "x", "y", "transform", "primary", "is-active" | ||
| </description> | ||
| </key> | ||
|
|
||
| </schema> | ||
| </schemalist> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
jeremypw marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /* | ||
| * SPDX-License-Identifier: GPL-2.0-or-later | ||
| * SPDX-FileCopyrightText: 2025 elementary, Inc. <https://elementary.io> | ||
| * | ||
| * Authored by: Leonardo Lemos <leonardolemos@live.com> | ||
| */ | ||
|
|
||
| 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<VirtualMonitor> virtual_monitors) { | ||
| if (virtual_monitors.size == 1) { | ||
|
jeremypw marked this conversation as resolved.
|
||
| // 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); | ||
| // Layouts format are 'a{sa{sa{sv}}}' | ||
| var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); | ||
| Variant? monitors = null; | ||
| if (layouts != null) { | ||
| monitors = layouts.lookup_value (layout_key, VariantType.VARDICT); | ||
| foreach (var virtual_monitor in virtual_monitors) { | ||
| Variant? props = monitors.lookup_value (virtual_monitor.id, VariantType.VARDICT); | ||
| if (props != null) { | ||
| int32 x = 0, y = 0; | ||
| uint32 t = 0; | ||
| bool p = false, e = false; | ||
| if (props.lookup ("x", "i", out x) && | ||
| props.lookup ("y", "i", out y) && | ||
| props.lookup ("transform", "u", out t) && | ||
| props.lookup ("primary", "b", out p) && | ||
| props.lookup ("enabled", "b", out e)) { | ||
|
|
||
| virtual_monitor.x = x; | ||
| virtual_monitor.y = y; | ||
| virtual_monitor.transform = t; | ||
| virtual_monitor.primary = p; | ||
| virtual_monitor.is_active = e; | ||
| } else { | ||
| warning ("property setting missing for monitor %s", virtual_monitor.get_display_name ()); | ||
| } | ||
| } else { | ||
| warning ("no property dictionary found for monitor.id %s", virtual_monitor.get_display_name ()); | ||
| } | ||
| } | ||
|
|
||
| return; | ||
| } else { | ||
| warning ("layout key %s not found", layout_key); | ||
| } | ||
|
|
||
| // If no layout found, we save the current layout to use later | ||
| save_layout (virtual_monitors); | ||
| } | ||
|
|
||
| public void save_layout (Gee.LinkedList<VirtualMonitor> virtual_monitors) { | ||
| var save_key = get_layout_key (virtual_monitors); | ||
|
|
||
| var monitor_dict = new VariantDict (); | ||
| foreach (var monitor in virtual_monitors) { | ||
| var props_dict = new VariantDict (); | ||
| props_dict.insert_value ("x", new Variant.int32 (monitor.x)); | ||
| props_dict.insert_value ("y", new Variant.int32 (monitor.y)); | ||
| props_dict.insert_value ("transform", new Variant.uint32 (monitor.transform)); | ||
| props_dict.insert_value ("primary", new Variant.boolean (monitor.primary)); | ||
| props_dict.insert_value ("enabled", new Variant.boolean (monitor.is_active)); | ||
| monitor_dict.insert_value (monitor.id, props_dict.end ()); | ||
| } | ||
|
|
||
| // Add or update the layouts setting | ||
| var layouts = settings.get_value (PREFERRED_MONITOR_LAYOUTS_KEY); | ||
| var layouts_dict = new VariantDict (layouts); | ||
| layouts_dict.insert_value (save_key, monitor_dict.end ()); | ||
|
|
||
| // Save to settings | ||
| //NOTE The variant yielded by VariantDict.end () always has type "a{sv}" | ||
| settings.set_value (PREFERRED_MONITOR_LAYOUTS_KEY, layouts_dict.end ()); | ||
| } | ||
|
|
||
| private string get_layout_key (Gee.LinkedList<VirtualMonitor> virtual_monitors) { | ||
| // Generate a unique key based on the virtual monitors' monitors hashes | ||
| //NOTE The key depends on the order of the list which will change depending on whether monitors are | ||
| // active or not (and possibly on the order they were connected). | ||
| //TODO Consider whether a more controlled key is needed | ||
| 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; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.