using System.Text.Json;
namespace XFESystem.Desktop;
public enum DesktopDisplayMode { Duplicate, Extend, SelectedOnly }
public sealed record DesktopDisplay(string Id, string Name, int NativeWidth, int NativeHeight);
public sealed record DesktopDisplayPosition(string Id, int X, int Y);
/// <summary>A bounded, immutable preference; coordinates are desktop layout units.</summary>
public sealed class DesktopDisplayConfiguration
{
public DesktopDisplayMode Mode { get; }
public string PrimaryId { get; }
public IReadOnlyList<DesktopDisplayPosition> Positions { get; }
public DesktopDisplayConfiguration(DesktopDisplayMode mode, string primaryId, IEnumerable<DesktopDisplayPosition> positions)
{ Mode = mode; PrimaryId = primaryId; Positions = Array.AsReadOnly(positions.Take(9).ToArray()); }
public DesktopDisplayConfiguration WithMode(DesktopDisplayMode mode) => new(mode, PrimaryId, Positions);
public DesktopDisplayConfiguration WithPrimary(string id) => new(Mode, id, Positions);
public DesktopDisplayConfiguration Move(string id, int x, int y) => new(Mode, PrimaryId, Positions.Select(p => p.Id == id ? new(id, x, y) : p));
private static string Quote(string value) => "\"" + JsonEncodedText.Encode(value).ToString() + "\"";
public string ToJson() => "{\"Version\":1,\"Mode\":" + Quote(Mode.ToString()) +
",\"Primary\":" + Quote(PrimaryId) + ",\"Positions\":[" +
string.Join(',', Positions.Select(p => "{\"Id\":" + Quote(p.Id) + ",\"X\":" + p.X.ToString(System.Globalization.CultureInfo.InvariantCulture) + ",\"Y\":" + p.Y.ToString(System.Globalization.CultureInfo.InvariantCulture) + "}")) + "]}";
public static DesktopDisplayConfiguration Parse(string text)
{
if (text.Length > 8192) throw new FormatException("Display settings exceed the supported size.");
using var document = JsonDocument.Parse(text, new JsonDocumentOptions { MaxDepth = 5 });
var root = document.RootElement;
RequireFields(root, "Version", "Mode", "Primary", "Positions");
if (root.GetProperty("Version").GetInt32() != 1 ||
!Enum.TryParse<DesktopDisplayMode>(root.GetProperty("Mode").GetString(), out var mode) || !Enum.IsDefined(mode))
throw new FormatException("Unsupported display settings version or mode.");
string primary = root.GetProperty("Primary").GetString() ?? "";
var positions = new List<DesktopDisplayPosition>();
var list = root.GetProperty("Positions");
if (list.ValueKind != JsonValueKind.Array || list.GetArrayLength() is < 1 or > 8) throw new FormatException("Invalid display count.");
foreach (var item in list.EnumerateArray())
{
RequireFields(item, "Id", "X", "Y");
string id = item.GetProperty("Id").GetString() ?? "";
if (id.Length is < 1 or > 128 || positions.Any(p => p.Id == id)) throw new FormatException("Invalid or duplicate display identity.");
positions.Add(new(id, item.GetProperty("X").GetInt32(), item.GetProperty("Y").GetInt32()));
}
return new(mode, primary, positions);
}
private static void RequireFields(JsonElement element, params string[] names)
{
if (element.ValueKind != JsonValueKind.Object) throw new FormatException("Display settings must be objects.");
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var property in element.EnumerateObject())
if (!names.Contains(property.Name, StringComparer.Ordinal) || !seen.Add(property.Name)) throw new FormatException("Unknown or duplicate display setting.");
if (seen.Count != names.Length) throw new FormatException("Incomplete display settings.");
}
}
public sealed record DesktopDisplayScreen(DesktopDisplay Display, DesktopBounds Bounds, bool IsPrimary);
/// <summary>Validates hardware identities, memory budget, overlap and reachable monitor edges.</summary>
public sealed class DesktopDisplayLayout
{
public IReadOnlyList<DesktopDisplayScreen> Screens { get; }
public DesktopDisplayScreen Primary { get; }
public int Width { get; }
public int Height { get; }
private DesktopDisplayLayout(DesktopDisplayScreen[] screens, int width, int height)
{ Screens = Array.AsReadOnly(screens); Primary = screens.Single(s => s.IsPrimary); Width = width; Height = height; }
public static DesktopDisplayConfiguration Default(IReadOnlyList<DesktopDisplay> displays, int scalePercent)
{
int x = 0, y = 0, rowHeight = 0;
var positions = displays.Select(d =>
{
int w = d.NativeWidth * 100 / scalePercent, h = d.NativeHeight * 100 / scalePercent;
if (x > 32768) { x = 0; y += rowHeight; rowHeight = 0; }
var position = new DesktopDisplayPosition(d.Id, x, y); x += w; rowHeight = Math.Max(rowHeight, h); return position;
}).ToArray();
return new(DesktopDisplayMode.Duplicate, displays[0].Id, positions);
}
public static DesktopDisplayLayout Create(IReadOnlyList<DesktopDisplay> displays, DesktopDisplayConfiguration configuration, int scalePercent)
{
if (displays.Count is < 1 or > 8 || scalePercent is < 100 or > 400 || !Enum.IsDefined(configuration.Mode) ||
displays.Any(d => string.IsNullOrEmpty(d.Id) || d.Id.Length > 128 || d.NativeWidth is < 320 or > 16384 ||
d.NativeHeight is < 200 or > 16384 || (long)d.NativeWidth * d.NativeHeight > 33_554_432) ||
displays.Select(d => d.Id).Distinct(StringComparer.Ordinal).Count() != displays.Count ||
!displays.Any(d => d.Id == configuration.PrimaryId) || configuration.Positions.Count != displays.Count)
throw new ArgumentException("The display configuration does not match the connected outputs.");
foreach (var display in displays)
if (configuration.Positions.Count(p => p.Id == display.Id) != 1) throw new ArgumentException("A display position is missing or duplicated.");
if (configuration.Positions.Any(p => p.X is < -32768 or > 32768 || p.Y is < -32768 or > 32768))
throw new ArgumentException("The display arrangement is outside the supported desktop.");
DesktopBounds Bounds(DesktopDisplay d)
{
var p = configuration.Positions.Single(p => p.Id == d.Id);
return new(p.X, p.Y, Math.Max(1, d.NativeWidth * 100 / scalePercent), Math.Max(1, d.NativeHeight * 100 / scalePercent));
}
var primary = displays.Single(d => d.Id == configuration.PrimaryId);
if (configuration.Mode != DesktopDisplayMode.Extend)
{
var b = Bounds(primary) with { X = 0, Y = 0 };
var active = configuration.Mode == DesktopDisplayMode.Duplicate ? displays : [primary];
return new(active.Select(d => new DesktopDisplayScreen(d, b, d.Id == primary.Id)).ToArray(), b.Width, b.Height);
}
var rectangles = displays.Select(Bounds).ToArray();
for (int i = 0; i < rectangles.Length; i++)
for (int j = 0; j < i; j++)
if (Overlap(rectangles[i], rectangles[j])) throw new ArgumentException("Displays cannot overlap in Extend mode.");
var reachable = new HashSet<int> { 0 };
for (int pass = 0; pass < displays.Count; pass++)
for (int i = 0; i < displays.Count; i++)
if (!reachable.Contains(i) && reachable.Any(j => Touches(rectangles[i], rectangles[j]))) reachable.Add(i);
if (reachable.Count != displays.Count) throw new ArgumentException("Arrange the displays so their edges touch.");
int left = rectangles.Min(b => b.X), top = rectangles.Min(b => b.Y);
int width = rectangles.Max(b => b.X + b.Width) - left, height = rectangles.Max(b => b.Y + b.Height) - top;
if ((long)width * scalePercent / 100 * (height * (long)scalePercent / 100) > 33_554_432)
throw new ArgumentException("This arrangement exceeds the desktop pixel budget.");
return new(displays.Select((d, i) => new DesktopDisplayScreen(d, rectangles[i] with { X = rectangles[i].X - left, Y = rectangles[i].Y - top }, d.Id == primary.Id)).ToArray(), width, height);
}
public (int X, int Y) ConstrainPointer(int x, int y)
{
(int X, int Y) closest = default;
long nearest = long.MaxValue;
for (int index = 0; index < Screens.Count; index++)
{
var b = Screens[index].Bounds;
if (b.Contains(x, y)) return (x, y);
var position = (X: Math.Clamp(x, b.X, b.X + b.Width - 1), Y: Math.Clamp(y, b.Y, b.Y + b.Height - 1));
long dx = (long)x - position.X, dy = (long)y - position.Y;
// Coordinates come from the bounded native desktop viewport.
long distance = dx * dx + dy * dy;
if (distance < nearest) { nearest = distance; closest = position; }
}
return closest;
}
public static bool Overlap(DesktopBounds a, DesktopBounds b) => a.X < b.X + b.Width && b.X < a.X + a.Width && a.Y < b.Y + b.Height && b.Y < a.Y + a.Height;
private static bool Touches(DesktopBounds a, DesktopBounds b) =>
(a.X + a.Width == b.X || b.X + b.Width == a.X) && Math.Max(a.Y, b.Y) < Math.Min(a.Y + a.Height, b.Y + b.Height) ||
(a.Y + a.Height == b.Y || b.Y + b.Height == a.Y) && Math.Max(a.X, b.X) < Math.Min(a.X + a.Width, b.X + b.Width);
}
public interface IDesktopDisplayManager
{
IReadOnlyList<DesktopDisplay> Displays { get; }
DesktopDisplayConfiguration Current { get; }
DesktopDisplayLayout Layout { get; }
int ScalePercent { get; }
string DetectionMessage { get; }
bool TryApply(DesktopDisplayConfiguration configuration, out string? error);
void Identify(bool visible);
void Detect();
event Action? Changed;
}
using System.Text.Json;
namespace XFESystem.Desktop;
public enum DesktopDisplayMode { Duplicate, Extend, SelectedOnly }
public sealed record DesktopDisplay(string Id, string Name, int NativeWidth, int NativeHeight);
public sealed record DesktopDisplayPosition(string Id, int X, int Y);
/// <summary>A bounded, immutable preference; coordinates are desktop layout units.</summary>
public sealed class DesktopDisplayConfiguration
{
public DesktopDisplayMode Mode { get; }
public string PrimaryId { get; }
public IReadOnlyList<DesktopDisplayPosition> Positions { get; }
public DesktopDisplayConfiguration(DesktopDisplayMode mode, string primaryId, IEnumerable<DesktopDisplayPosition> positions)
{ Mode = mode; PrimaryId = primaryId; Positions = Array.AsReadOnly(positions.Take(9).ToArray()); }
public DesktopDisplayConfiguration WithMode(DesktopDisplayMode mode) => new(mode, PrimaryId, Positions);
public DesktopDisplayConfiguration WithPrimary(string id) => new(Mode, id, Positions);
public DesktopDisplayConfiguration Move(string id, int x, int y) => new(Mode, PrimaryId, Positions.Select(p => p.Id == id ? new(id, x, y) : p));
private static string Quote(string value) => "\"" + JsonEncodedText.Encode(value).ToString() + "\"";
public string ToJson() => "{\"Version\":1,\"Mode\":" + Quote(Mode.ToString()) +
",\"Primary\":" + Quote(PrimaryId) + ",\"Positions\":[" +
string.Join(',', Positions.Select(p => "{\"Id\":" + Quote(p.Id) + ",\"X\":" + p.X.ToString(System.Globalization.CultureInfo.InvariantCulture) + ",\"Y\":" + p.Y.ToString(System.Globalization.CultureInfo.InvariantCulture) + "}")) + "]}";
public static DesktopDisplayConfiguration Parse(string text)
{
if (text.Length > 8192) throw new FormatException("Display settings exceed the supported size.");
using var document = JsonDocument.Parse(text, new JsonDocumentOptions { MaxDepth = 5 });
var root = document.RootElement;
RequireFields(root, "Version", "Mode", "Primary", "Positions");
if (root.GetProperty("Version").GetInt32() != 1 ||
!Enum.TryParse<DesktopDisplayMode>(root.GetProperty("Mode").GetString(), out var mode) || !Enum.IsDefined(mode))
throw new FormatException("Unsupported display settings version or mode.");
string primary = root.GetProperty("Primary").GetString() ?? "";
var positions = new List<DesktopDisplayPosition>();
var list = root.GetProperty("Positions");
if (list.ValueKind != JsonValueKind.Array || list.GetArrayLength() is < 1 or > 8) throw new FormatException("Invalid display count.");
foreach (var item in list.EnumerateArray())
{
RequireFields(item, "Id", "X", "Y");
string id = item.GetProperty("Id").GetString() ?? "";
if (id.Length is < 1 or > 128 || positions.Any(p => p.Id == id)) throw new FormatException("Invalid or duplicate display identity.");
positions.Add(new(id, item.GetProperty("X").GetInt32(), item.GetProperty("Y").GetInt32()));
}
return new(mode, primary, positions);
}
private static void RequireFields(JsonElement element, params string[] names)
{
if (element.ValueKind != JsonValueKind.Object) throw new FormatException("Display settings must be objects.");
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var property in element.EnumerateObject())
if (!names.Contains(property.Name, StringComparer.Ordinal) || !seen.Add(property.Name)) throw new FormatException("Unknown or duplicate display setting.");
if (seen.Count != names.Length) throw new FormatException("Incomplete display settings.");
}
}
public sealed record DesktopDisplayScreen(DesktopDisplay Display, DesktopBounds Bounds, bool IsPrimary);
/// <summary>Validates hardware identities, memory budget, overlap and reachable monitor edges.</summary>
public sealed class DesktopDisplayLayout
{
public IReadOnlyList<DesktopDisplayScreen> Screens { get; }
public DesktopDisplayScreen Primary { get; }
public int Width { get; }
public int Height { get; }
private DesktopDisplayLayout(DesktopDisplayScreen[] screens, int width, int height)
{ Screens = Array.AsReadOnly(screens); Primary = screens.Single(s => s.IsPrimary); Width = width; Height = height; }
public static DesktopDisplayConfiguration Default(IReadOnlyList<DesktopDisplay> displays, int scalePercent)
{
int x = 0, y = 0, rowHeight = 0;
var positions = displays.Select(d =>
{
int w = d.NativeWidth * 100 / scalePercent, h = d.NativeHeight * 100 / scalePercent;
if (x > 32768) { x = 0; y += rowHeight; rowHeight = 0; }
var position = new DesktopDisplayPosition(d.Id, x, y); x += w; rowHeight = Math.Max(rowHeight, h); return position;
}).ToArray();
return new(DesktopDisplayMode.Duplicate, displays[0].Id, positions);
}
public static DesktopDisplayLayout Create(IReadOnlyList<DesktopDisplay> displays, DesktopDisplayConfiguration configuration, int scalePercent)
{
if (displays.Count is < 1 or > 8 || scalePercent is < 100 or > 400 || !Enum.IsDefined(configuration.Mode) ||
displays.Any(d => string.IsNullOrEmpty(d.Id) || d.Id.Length > 128 || d.NativeWidth is < 320 or > 16384 ||
d.NativeHeight is < 200 or > 16384 || (long)d.NativeWidth * d.NativeHeight > 33_554_432) ||
displays.Select(d => d.Id).Distinct(StringComparer.Ordinal).Count() != displays.Count ||
!displays.Any(d => d.Id == configuration.PrimaryId) || configuration.Positions.Count != displays.Count)
throw new ArgumentException("The display configuration does not match the connected outputs.");
foreach (var display in displays)
if (configuration.Positions.Count(p => p.Id == display.Id) != 1) throw new ArgumentException("A display position is missing or duplicated.");
if (configuration.Positions.Any(p => p.X is < -32768 or > 32768 || p.Y is < -32768 or > 32768))
throw new ArgumentException("The display arrangement is outside the supported desktop.");
DesktopBounds Bounds(DesktopDisplay d)
{
var p = configuration.Positions.Single(p => p.Id == d.Id);
return new(p.X, p.Y, Math.Max(1, d.NativeWidth * 100 / scalePercent), Math.Max(1, d.NativeHeight * 100 / scalePercent));
}
var primary = displays.Single(d => d.Id == configuration.PrimaryId);
if (configuration.Mode != DesktopDisplayMode.Extend)
{
var b = Bounds(primary) with { X = 0, Y = 0 };
var active = configuration.Mode == DesktopDisplayMode.Duplicate ? displays : [primary];
return new(active.Select(d => new DesktopDisplayScreen(d, b, d.Id == primary.Id)).ToArray(), b.Width, b.Height);
}
var rectangles = displays.Select(Bounds).ToArray();
for (int i = 0; i < rectangles.Length; i++)
for (int j = 0; j < i; j++)
if (Overlap(rectangles[i], rectangles[j])) throw new ArgumentException("Displays cannot overlap in Extend mode.");
var reachable = new HashSet<int> { 0 };
for (int pass = 0; pass < displays.Count; pass++)
for (int i = 0; i < displays.Count; i++)
if (!reachable.Contains(i) && reachable.Any(j => Touches(rectangles[i], rectangles[j]))) reachable.Add(i);
if (reachable.Count != displays.Count) throw new ArgumentException("Arrange the displays so their edges touch.");
int left = rectangles.Min(b => b.X), top = rectangles.Min(b => b.Y);
int width = rectangles.Max(b => b.X + b.Width) - left, height = rectangles.Max(b => b.Y + b.Height) - top;
if ((long)width * scalePercent / 100 * (height * (long)scalePercent / 100) > 33_554_432)
throw new ArgumentException("This arrangement exceeds the desktop pixel budget.");
return new(displays.Select((d, i) => new DesktopDisplayScreen(d, rectangles[i] with { X = rectangles[i].X - left, Y = rectangles[i].Y - top }, d.Id == primary.Id)).ToArray(), width, height);
}
public (int X, int Y) ConstrainPointer(int x, int y)
{
(int X, int Y) closest = default;
long nearest = long.MaxValue;
for (int index = 0; index < Screens.Count; index++)
{
var b = Screens[index].Bounds;
if (b.Contains(x, y)) return (x, y);
var position = (X: Math.Clamp(x, b.X, b.X + b.Width - 1), Y: Math.Clamp(y, b.Y, b.Y + b.Height - 1));
long dx = (long)x - position.X, dy = (long)y - position.Y;
// Coordinates come from the bounded native desktop viewport.
long distance = dx * dx + dy * dy;
if (distance < nearest) { nearest = distance; closest = position; }
}
return closest;
}
public static bool Overlap(DesktopBounds a, DesktopBounds b) => a.X < b.X + b.Width && b.X < a.X + a.Width && a.Y < b.Y + b.Height && b.Y < a.Y + a.Height;
private static bool Touches(DesktopBounds a, DesktopBounds b) =>
(a.X + a.Width == b.X || b.X + b.Width == a.X) && Math.Max(a.Y, b.Y) < Math.Min(a.Y + a.Height, b.Y + b.Height) ||
(a.Y + a.Height == b.Y || b.Y + b.Height == a.Y) && Math.Max(a.X, b.X) < Math.Min(a.X + a.Width, b.X + b.Width);
}
public interface IDesktopDisplayManager
{
IReadOnlyList<DesktopDisplay> Displays { get; }
DesktopDisplayConfiguration Current { get; }
DesktopDisplayLayout Layout { get; }
int ScalePercent { get; }
string DetectionMessage { get; }
bool TryApply(DesktopDisplayConfiguration configuration, out string? error);
void Identify(bool visible);
void Detect();
event Action? Changed;
}