diff --git a/.task/checksum/build b/.task/checksum/build
new file mode 100644
index 0000000..3a6f5c8
--- /dev/null
+++ b/.task/checksum/build
@@ -0,0 +1 @@
+59580c486f374847db3eaf5ef6840550
diff --git a/.task/checksum/restore b/.task/checksum/restore
new file mode 100644
index 0000000..f3e596e
--- /dev/null
+++ b/.task/checksum/restore
@@ -0,0 +1 @@
+249f80eb30502a59abbbf97324e45dbe
diff --git a/DisplayManager.Linux/Program.cs b/DisplayManager.Linux/Program.cs
index efaed5a..99c9919 100644
--- a/DisplayManager.Linux/Program.cs
+++ b/DisplayManager.Linux/Program.cs
@@ -1,6 +1,6 @@
using Gregghz.DisplayManager.Services;
using Gregghz.DisplayManager.Services.Implementations;
-using Gregghz.DisplayManager.UI.Cli;
+using Gregghz.DisplayManager.Cli;
using Gtk;
namespace Gregghz.DisplayManager;
diff --git a/DisplayManager.Mac/Program.cs b/DisplayManager.Mac/Program.cs
index 39bf639..2d04726 100644
--- a/DisplayManager.Mac/Program.cs
+++ b/DisplayManager.Mac/Program.cs
@@ -1,5 +1,5 @@
using Gregghz.DisplayManager.Services.Implementations;
-using Gregghz.DisplayManager.UI.Cli;
+using Gregghz.DisplayManager.Cli;
namespace Gregghz.DisplayManager;
diff --git a/DisplayManager.Windows/App.xaml b/DisplayManager.Windows/App.xaml
deleted file mode 100644
index bdd66f8..0000000
--- a/DisplayManager.Windows/App.xaml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/DisplayManager.Windows/App.xaml.cs b/DisplayManager.Windows/App.xaml.cs
deleted file mode 100644
index 822c96a..0000000
--- a/DisplayManager.Windows/App.xaml.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Gregghz.DisplayManager.Services.Implementations;
-using Gregghz.DisplayManager.UI.Cli;
-using Gregghz.DisplayManager.Windows.Services.Implementations;
-using Gregghz.DisplayManager.Windows.Views;
-using Microsoft.UI.Xaml;
-
-namespace Gregghz.DisplayManager.Windows;
-
-public partial class App
-{
- public App()
- {
- InitializeComponent();
- }
-
-
- protected override void OnLaunched(LaunchActivatedEventArgs args)
- {
- var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- var path = Path.Combine(appDataPath, "DisplayManager");
- Directory.CreateDirectory(path);
-
- // @TODO: DI
- var layoutService = new FileSystemLayoutService(path);
- var displayService = new WindowsDisplayService();
-
- var cliArgs = Environment.GetCommandLineArgs();
-
- if (cliArgs.Length == 0 || cliArgs[0].EndsWith(".dll"))
- {
- var mainWindow = new MainWindow(displayService, layoutService);
- mainWindow.Activate();
- }
- else
- {
- // cli stuff
- var console = new ConsoleMain(displayService, layoutService);
- console.Run(cliArgs);
- // Current.Shutdown();
- }
- }
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/DisplayManager.Windows.csproj b/DisplayManager.Windows/DisplayManager.Windows.csproj
index cccd98f..85e1365 100644
--- a/DisplayManager.Windows/DisplayManager.Windows.csproj
+++ b/DisplayManager.Windows/DisplayManager.Windows.csproj
@@ -1,34 +1,22 @@
- WinExe
+ Exe
net8.0-windows10.0.19041.0
10.0.19041.0
10.0.17763
Gregghz.DisplayManager.Windows
- app.manifest
x86;x64
win-x86;win-x64
win-$(Platform).pubxml
- true
- true
- None
- false
- false
enable
enable
-
-
-
-
-
-
diff --git a/DisplayManager.Windows/Extensions/SettingsExtensions.cs b/DisplayManager.Windows/Extensions/SettingsExtensions.cs
index 9523703..df4b36d 100644
--- a/DisplayManager.Windows/Extensions/SettingsExtensions.cs
+++ b/DisplayManager.Windows/Extensions/SettingsExtensions.cs
@@ -5,11 +5,12 @@ namespace Gregghz.DisplayManager.Windows.Extensions;
public static class SettingsExtensions
{
- public static Settings FromDevMode(string deviceId, in DEVMODE mode)
+ public static Settings FromDevMode(string deviceId, bool isConnected, in DEVMODE mode)
{
return new Settings(
deviceId,
mode.dmPositionX == 0 && mode.dmPositionY == 0,
+ isConnected,
new Position(mode.dmPositionX, mode.dmPositionY),
new Resolution(mode.dmPelsWidth, mode.dmPelsHeight),
(Orientation)mode.dmDisplayOrientation,
@@ -19,6 +20,12 @@ public static Settings FromDevMode(string deviceId, in DEVMODE mode)
public static DEVMODE UpdateDevMode(this Settings settings, ref DEVMODE mode)
{
+ mode.dmFields |= Constants.DM_POSITION |
+ Constants.DM_PELSWIDTH |
+ Constants.DM_PELSHEIGHT |
+ Constants.DM_DISPLAYORIENTATION |
+ Constants.DM_DISPLAYFREQUENCY;
+
if (settings.IsPrimary)
{
mode.dmPositionX = 0;
diff --git a/DisplayManager.Windows/Native/Constants.cs b/DisplayManager.Windows/Native/Constants.cs
index 7dbf28d..4e5813e 100644
--- a/DisplayManager.Windows/Native/Constants.cs
+++ b/DisplayManager.Windows/Native/Constants.cs
@@ -3,19 +3,23 @@ namespace Gregghz.DisplayManager.Windows.Native;
public static class Constants
{
public const int ENUM_CURRENT_SETTINGS = -1;
- public const uint EDD_GET_DEVICE_INTERFACE_NAME = 0x00000001;
+ public const int ENUM_REGISTRY_SETTINGS = -2;
+
+ public const int DISPLAY_DEVICE_ATTACHED_TO_DESKTOP = 0x00000001;
+ public const int DISPLAY_DEVICE_PRIMARY_DEVICE = 0x00000004;
+
+ public const int DM_POSITION = 0x00000020;
+ public const int DM_DISPLAYORIENTATION = 0x00000080;
+ public const int DM_PELSWIDTH = 0x00080000;
+ public const int DM_PELSHEIGHT = 0x00100000;
+ public const int DM_DISPLAYFREQUENCY = 0x00400000;
+
+ public const uint SDC_APPLY = 0x00000080;
+ public const uint SDC_TOPOLOGY_EXTEND = 0x00000004;
public const int CDS_NONE = 0x00;
public const int CDS_UPDATEREGISTRY = 0x01;
- public const int CDS_TEST = 0x02;
- public const int CDS_FULLSCREEN = 0x04;
- public const int CDS_GLOBAL = 0x08;
public const int CDS_SET_PRIMARY = 0x10;
- public const int CDS_VIDEOPARAMETERS = 0x20;
- public const int CDS_ENABLE_UNSAFE_MODES = 0x100;
- public const int CDS_DISABLE_UNSAFE_MODES = 0x200;
- public const int CDS_RESET = 0x40000000;
- public const int CDS_RESET_EX = 0x20000000;
public const int CDS_NORESET = 0x10000000;
public const int DISP_CHANGE_SUCCESSFUL = 0;
diff --git a/DisplayManager.Windows/Native/DisplayAPI.cs b/DisplayManager.Windows/Native/DisplayAPI.cs
new file mode 100644
index 0000000..5f0239d
--- /dev/null
+++ b/DisplayManager.Windows/Native/DisplayAPI.cs
@@ -0,0 +1,33 @@
+using System.Runtime.InteropServices;
+
+namespace Gregghz.DisplayManager.Windows.Native;
+
+public static class DisplayApi
+{
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern bool EnumDisplayDevices(
+ string lpDevice,
+ uint iDevNum,
+ ref DISPLAY_DEVICE lpDisplayDevice,
+ uint dwFlags);
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct DISPLAY_DEVICE
+ {
+ public int cb;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string DeviceName;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string DeviceString;
+
+ public int StateFlags;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string DeviceID;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string DeviceKey;
+ }
+}
diff --git a/DisplayManager.Windows/Native/DisplayDevice.cs b/DisplayManager.Windows/Native/DisplayDevice.cs
deleted file mode 100644
index 1dd85ec..0000000
--- a/DisplayManager.Windows/Native/DisplayDevice.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Gregghz.DisplayManager.Windows.Native;
-
-[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
-public struct DISPLAY_DEVICE
-{
- public int cb;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
- public string DeviceName;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- public string DeviceString;
- public uint StateFlags;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- public string DeviceID;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- public string DeviceKey;
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/Native/User32.cs b/DisplayManager.Windows/Native/User32.cs
index 98b2811..d778a6d 100644
--- a/DisplayManager.Windows/Native/User32.cs
+++ b/DisplayManager.Windows/Native/User32.cs
@@ -25,6 +25,14 @@ public static extern int ChangeDisplaySettingsEx(string? lpszDeviceName, IntPtr
public static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
- public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice,
+ public static extern bool EnumDisplayDevices(string? lpDevice, uint iDevNum, ref DisplayApi.DISPLAY_DEVICE lpDisplayDevice,
uint dwFlags);
+
+ [DllImport("user32.dll")]
+ public static extern int SetDisplayConfig(
+ uint numPathArrayElements,
+ IntPtr pathArray,
+ uint numModeInfoArrayElements,
+ IntPtr modeInfoArray,
+ uint flags);
}
\ No newline at end of file
diff --git a/DisplayManager.Windows/Program.cs b/DisplayManager.Windows/Program.cs
new file mode 100644
index 0000000..a30aba1
--- /dev/null
+++ b/DisplayManager.Windows/Program.cs
@@ -0,0 +1,15 @@
+using Gregghz.DisplayManager.Services.Implementations;
+using Gregghz.DisplayManager.Cli;
+using Gregghz.DisplayManager.Windows.Services.Implementations;
+
+var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+var path = Path.Combine(appDataPath, "DisplayManager");
+Directory.CreateDirectory(path);
+
+var layoutService = new FileSystemLayoutService(path);
+var displayService = new WindowsDisplayService();
+
+var argsToRun = args.Length == 0 ? ["--help"] : args;
+
+var console = new ConsoleMain(displayService, layoutService);
+console.Run(argsToRun);
diff --git a/DisplayManager.Windows/Services/Implementations/WindowsDisplayService.cs b/DisplayManager.Windows/Services/Implementations/WindowsDisplayService.cs
index 2507e9b..fa0938b 100644
--- a/DisplayManager.Windows/Services/Implementations/WindowsDisplayService.cs
+++ b/DisplayManager.Windows/Services/Implementations/WindowsDisplayService.cs
@@ -8,27 +8,39 @@ namespace Gregghz.DisplayManager.Windows.Services.Implementations;
public class WindowsDisplayService : IDisplayService
{
+ private const string AnsiCyan = "\u001b[36m";
+ private const string AnsiGreen = "\u001b[32m";
+ private const string AnsiRed = "\u001b[31m";
+ private const string AnsiReset = "\u001b[0m";
+
+ private static DEVMODE CreateDevMode()
+ {
+ return new DEVMODE
+ {
+ dmSize = (short)Marshal.SizeOf()
+ };
+ }
+
public Layout GetDisplayLayout()
{
List foundSettings = [];
var deviceMap = GetDeviceMap();
- bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
+ foreach (var device in GetDisplayDevices())
{
- var mi = new MONITORINFOEX();
- User32.GetMonitorInfo(hMonitor, mi);
- var deviceName = new string(mi.szDevice).TrimEnd((char)0);
-
- var mode = new DEVMODE();
- User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode);
+ var deviceName = device.DeviceName;
+ if (!deviceMap.TryGetValue(deviceName, out var id)) continue;
- var id = deviceMap[deviceName];
- foundSettings.Add(SettingsExtensions.FromDevMode(id, mode));
+ var mode = CreateDevMode();
+ var hasCurrentMode = User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode);
+ var hasRegistryMode = hasCurrentMode ||
+ User32.EnumDisplaySettings(deviceName, Constants.ENUM_REGISTRY_SETTINGS, ref mode);
+ if (!hasRegistryMode) continue;
- return true; // Continue enumeration
+ var isConnected = (device.StateFlags & Constants.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0;
+ foundSettings.Add(SettingsExtensions.FromDevMode(id, isConnected, mode));
}
- User32.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
return new Layout(foundSettings);
}
@@ -36,23 +48,28 @@ public string GetMonitorInfo()
{
var monitorInfo = "";
- bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
+ foreach (var device in GetDisplayDevices())
{
- var mi = new MONITORINFOEX();
- User32.GetMonitorInfo(hMonitor, mi);
- var deviceName = new string(mi.szDevice).TrimEnd((char)0);
-
- var mode = new DEVMODE();
- User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode);
-
- monitorInfo +=
- $"Monitor: {deviceName} ({hMonitor}) - Bounds: {mi.rcMonitor.left}, {mi.rcMonitor.top}, {mi.rcMonitor.right}, {mi.rcMonitor.bottom}\n";
+ var deviceName = device.DeviceName;
+
+ var mode = CreateDevMode();
+ var hasCurrentMode = User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode);
+ var hasRegistryMode = hasCurrentMode ||
+ User32.EnumDisplaySettings(deviceName, Constants.ENUM_REGISTRY_SETTINGS, ref mode);
+ if (!hasRegistryMode) continue;
+
+ var connectionState =
+ (device.StateFlags & Constants.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0 ? "Connected" : "Disconnected";
+ var stateLabel = connectionState == "Disconnected"
+ ? $"{AnsiRed}{connectionState}{AnsiReset}"
+ : $"{AnsiGreen}{connectionState}{AnsiReset}";
+ var isPrimary = (device.StateFlags & Constants.DISPLAY_DEVICE_PRIMARY_DEVICE) != 0;
+ var primaryLabel = isPrimary ? $"{AnsiCyan}PRIMARY{AnsiReset}" : "SECONDARY";
+
+ monitorInfo += $"Monitor: {deviceName} - State: {stateLabel} - {primaryLabel}\n";
monitorInfo += $"{mode}\r\n\r\n";
- return true; // Continue enumeration
}
- User32.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);
-
return monitorInfo;
}
@@ -75,22 +92,24 @@ public async Task ApplyLayout(Layout layout)
foreach (var s in settings) await UpdateSettings(s.DeviceId, s);
- ApplySettings();
+ var applyResult = ApplySettings();
+ if (applyResult != Constants.DISP_CHANGE_SUCCESSFUL)
+ await Console.Error.WriteLineAsync($"Desktop apply returned {GetDisplayChangeName(applyResult)} ({applyResult}).");
}
- private IEnumerable GetDisplayDevices()
+ private IEnumerable GetDisplayDevices()
{
- var d = new DISPLAY_DEVICE();
- d.cb = Marshal.SizeOf(d);
+ var monitor = new DisplayApi.DISPLAY_DEVICE();
+ monitor.cb = Marshal.SizeOf(monitor);
- List result = new();
+ List result = new();
- for (uint id = 0; User32.EnumDisplayDevices(null, id, ref d, 0); id++)
+ for (uint id = 0; User32.EnumDisplayDevices(null, id, ref monitor, 0); id++)
{
- result.Add(d);
+ result.Add(monitor);
- d = new DISPLAY_DEVICE();
- d.cb = Marshal.SizeOf(d);
+ monitor = new DisplayApi.DISPLAY_DEVICE();
+ monitor.cb = Marshal.SizeOf(monitor);
}
return result;
@@ -99,21 +118,98 @@ private IEnumerable GetDisplayDevices()
private async Task UpdateSettings(string deviceId, Settings settings)
{
var deviceMap = GetDeviceMap();
- var deviceName = deviceMap[deviceId]; // @TODO: handle missing ID
+ if (!deviceMap.TryGetValue(deviceId, out var deviceName))
+ {
+ await Console.Error.WriteLineAsync($"Skipping missing display device '{deviceId}'.");
+ return Constants.DISP_CHANGE_BADPARAM;
+ }
- var mode = new DEVMODE();
- await Task.Run(() => User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode));
+ var isCurrentlyConnected = IsDeviceAttachedToDesktop(deviceName);
+ var shouldAttemptAttach = !isCurrentlyConnected && settings.IsConnected;
+
+ if (shouldAttemptAttach)
+ {
+ var topologyResult = EnsureExtendedTopology();
+ if (topologyResult != Constants.DISP_CHANGE_SUCCESSFUL)
+ {
+ await Console.Error.WriteLineAsync(
+ $"Topology extend returned {GetDisplayChangeName(topologyResult)} ({topologyResult}) before attaching '{deviceName}'.");
+ }
+ }
- settings.UpdateDevMode(ref mode);
+ var mode = CreateDevMode();
+ var hasCurrentMode = await Task.Run(() => User32.EnumDisplaySettings(deviceName, Constants.ENUM_CURRENT_SETTINGS, ref mode));
+ if (!hasCurrentMode)
+ await Task.Run(() => User32.EnumDisplaySettings(deviceName, Constants.ENUM_REGISTRY_SETTINGS, ref mode));
+ // If both fail the monitor has no base mode; apply entirely from saved settings.
- uint dwFlags = Constants.CDS_UPDATEREGISTRY | Constants.CDS_NORESET;
+ if (settings.IsConnected)
+ {
+ settings.UpdateDevMode(ref mode);
+ }
+ else
+ {
+ // Disable this output in the loaded layout.
+ mode.dmFields = Constants.DM_POSITION |
+ Constants.DM_PELSWIDTH |
+ Constants.DM_PELSHEIGHT;
+ mode.dmPositionX = 0;
+ mode.dmPositionY = 0;
+ mode.dmPelsWidth = 0;
+ mode.dmPelsHeight = 0;
+ }
+
+ // Stage all monitors for one final desktop apply.
+ uint dwFlags = (uint)(Constants.CDS_UPDATEREGISTRY | Constants.CDS_NORESET);
if (settings.IsPrimary) dwFlags |= Constants.CDS_SET_PRIMARY;
var result = await Task.Run(() =>
User32.ChangeDisplaySettingsEx(deviceName, ref mode, IntPtr.Zero, dwFlags, IntPtr.Zero));
+
+ if (result != Constants.DISP_CHANGE_SUCCESSFUL)
+ await Console.Error.WriteLineAsync(
+ $"Apply failed for '{deviceName}' ({deviceId}): {GetDisplayChangeName(result)} ({result}).");
+
return result;
}
+ private static int EnsureExtendedTopology()
+ {
+ return User32.SetDisplayConfig(
+ 0,
+ IntPtr.Zero,
+ 0,
+ IntPtr.Zero,
+ Constants.SDC_APPLY | Constants.SDC_TOPOLOGY_EXTEND);
+ }
+
+ private bool IsDeviceAttachedToDesktop(string deviceName)
+ {
+ foreach (var device in GetDisplayDevices())
+ {
+ if (!string.Equals(device.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase)) continue;
+ return (device.StateFlags & Constants.DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0;
+ }
+
+ return false;
+ }
+
+ private static string GetDisplayChangeName(int result)
+ {
+ return result switch
+ {
+ Constants.DISP_CHANGE_SUCCESSFUL => nameof(Constants.DISP_CHANGE_SUCCESSFUL),
+ Constants.DISP_CHANGE_RESTART => nameof(Constants.DISP_CHANGE_RESTART),
+ Constants.DISP_CHANGE_FAILED => nameof(Constants.DISP_CHANGE_FAILED),
+ Constants.DISP_CHANGE_BADMODE => nameof(Constants.DISP_CHANGE_BADMODE),
+ Constants.DISP_CHANGE_NOTUPDATED => nameof(Constants.DISP_CHANGE_NOTUPDATED),
+ Constants.DISP_CHANGE_BADFLAGS => nameof(Constants.DISP_CHANGE_BADFLAGS),
+ Constants.DISP_CHANGE_BADPARAM => nameof(Constants.DISP_CHANGE_BADPARAM),
+ Constants.DISP_CHANGE_BADDUALVIEW => nameof(Constants.DISP_CHANGE_BADDUALVIEW),
+ _ => "UNKNOWN_RESULT"
+ };
+ }
+
private static int ApplySettings(string? deviceName = null)
{
return User32.ChangeDisplaySettingsEx(
diff --git a/DisplayManager.Windows/ViewModels/ApplyLayoutCommand.cs b/DisplayManager.Windows/ViewModels/ApplyLayoutCommand.cs
deleted file mode 100644
index 8732567..0000000
--- a/DisplayManager.Windows/ViewModels/ApplyLayoutCommand.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Windows.Input;
-using Gregghz.DisplayManager.Services;
-
-namespace Gregghz.DisplayManager.Windows.ViewModels;
-
-public class ApplyLayoutCommand(IDisplayService displayService, ILayoutService layoutService) : ICommand
-{
- public bool CanExecute(object? parameter)
- {
- return true;
- }
-
- public async void Execute(object? parameter)
- {
- if (parameter is not string layoutName) return;
- var layout = await layoutService.GetLayout(layoutName);
- if (layout is not null) await displayService.ApplyLayout(layout);
- // @TODO: what to do if layout is null?
- }
-
- public event EventHandler? CanExecuteChanged;
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/ViewModels/MainWindowViewModel.cs b/DisplayManager.Windows/ViewModels/MainWindowViewModel.cs
deleted file mode 100644
index 74edab3..0000000
--- a/DisplayManager.Windows/ViewModels/MainWindowViewModel.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Windows.Input;
-using Gregghz.DisplayManager.Model;
-using Gregghz.DisplayManager.Services;
-
-namespace Gregghz.DisplayManager.Windows.ViewModels;
-
-public class MainWindowViewModel : INotifyPropertyChanged
-{
- private readonly ILayoutService _layoutService;
- public readonly ICommand ApplyLayoutCommand;
- public readonly OpenSavePopupCommand OpenSavePopupCommand;
- public readonly SaveLayoutCommand SaveLayoutCommand;
- private bool _applyEnabled;
- private string? _selectedLayout;
-
- public MainWindowViewModel(IDisplayService displayService, ILayoutService layoutService)
- {
- _layoutService = layoutService;
- ApplyLayoutCommand = new ApplyLayoutCommand(displayService, layoutService);
- OpenSavePopupCommand = new OpenSavePopupCommand(displayService);
- SaveLayoutCommand = new SaveLayoutCommand(displayService, layoutService);
-
- Layouts.CollectionChanged += (_, args) =>
- {
- var newCount = args.NewItems?.Count ?? 0;
- var oldCount = args.OldItems?.Count ?? 0;
- if (newCount != oldCount) LayoutCountChanged?.Invoke(this, newCount);
- };
-
- PopulateLayouts();
- }
-
- public ObservableCollection Layouts { get; } = new();
-
- public bool ApplyEnabled
- {
- get => _applyEnabled;
- set
- {
- if (_applyEnabled != value)
- {
- _applyEnabled = value;
- OnPropertyChanged();
- }
- }
- }
-
- public string? SelectedLayout
- {
- get => _selectedLayout;
- set
- {
- if (_selectedLayout != value)
- {
- _selectedLayout = value;
- ApplyEnabled = value is not null;
- TriggerLayoutSelected();
-
- OnPropertyChanged();
- }
- }
- }
-
- public event PropertyChangedEventHandler? PropertyChanged;
-
- private async void PopulateLayouts()
- {
- var layouts = await _layoutService.GetSavedLayouts();
- foreach (var layout in layouts) Layouts.Add(layout);
- }
-
- private async void TriggerLayoutSelected()
- {
- if (_selectedLayout is null) return;
- var layout = await _layoutService.GetLayout(_selectedLayout);
- if (layout is not null) LayoutSelected?.Invoke(this, layout);
- }
-
- public void SubscribeToLayoutCountChanged(Action onChanged)
- {
- LayoutCountChanged += (_, count) => onChanged(count);
- onChanged(Layouts.Count);
- }
-
- public void SubscribeToLayoutSelected(Action onSelected)
- {
- LayoutSelected += (_, layout) => onSelected(layout);
- }
-
- public void SubscribeToLayoutSaved(Action> onSaved)
- {
- SaveLayoutCommand.LayoutSaved += async (_, _) =>
- {
- var layouts = await _layoutService.GetSavedLayouts();
- onSaved(layouts);
- };
- }
-
- public event EventHandler? LayoutCountChanged;
- public event EventHandler? LayoutSelected;
-
- public void SubscribeToSaveDialogRequested(EventHandler handler)
- {
- OpenSavePopupCommand.SubscribeToSaveDialogRequest(handler);
- }
-
- protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null)
- {
- if (EqualityComparer.Default.Equals(field, value)) return false;
- field = value;
- OnPropertyChanged(propertyName);
- return true;
- }
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/ViewModels/OpenSavePopupCommand.cs b/DisplayManager.Windows/ViewModels/OpenSavePopupCommand.cs
deleted file mode 100644
index 83c396c..0000000
--- a/DisplayManager.Windows/ViewModels/OpenSavePopupCommand.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Windows.Input;
-using Gregghz.DisplayManager.Model;
-using Gregghz.DisplayManager.Services;
-
-namespace Gregghz.DisplayManager.Windows.ViewModels;
-
-public class OpenSavePopupCommand(IDisplayService displayService) : ICommand
-{
- public bool CanExecute(object? parameter)
- {
- return true;
- }
-
- public void Execute(object? parameter)
- {
- var currentLayout = displayService.GetDisplayLayout();
- SaveDialogRequested?.Invoke(this, currentLayout);
- }
-
- public event EventHandler? CanExecuteChanged;
-
- public void SubscribeToSaveDialogRequest(EventHandler f)
- {
- SaveDialogRequested += f;
- }
-
- public event EventHandler? SaveDialogRequested;
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/ViewModels/SaveDialogViewModel.cs b/DisplayManager.Windows/ViewModels/SaveDialogViewModel.cs
deleted file mode 100644
index ffdcce7..0000000
--- a/DisplayManager.Windows/ViewModels/SaveDialogViewModel.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using Microsoft.UI.Xaml.Controls;
-
-namespace Gregghz.DisplayManager.Windows.ViewModels;
-
-public class SaveDialogViewModel(Canvas canvas) : INotifyPropertyChanged
-{
- private string? _layoutName;
-
- public string? LayoutName
- {
- get => _layoutName;
- set
- {
- if (_layoutName == value) return;
- _layoutName = value;
- OnPropertyChanged();
- }
- }
-
- public Canvas Canvas
- {
- get => canvas;
- set
- {
- canvas = value;
- OnPropertyChanged();
- }
- }
-
- public event PropertyChangedEventHandler? PropertyChanged;
-
- protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null)
- {
- if (EqualityComparer.Default.Equals(field, value)) return false;
- field = value;
- OnPropertyChanged(propertyName);
- return true;
- }
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/ViewModels/SaveLayoutCommand.cs b/DisplayManager.Windows/ViewModels/SaveLayoutCommand.cs
deleted file mode 100644
index 9db557d..0000000
--- a/DisplayManager.Windows/ViewModels/SaveLayoutCommand.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Windows.Input;
-using Gregghz.DisplayManager.Services;
-
-namespace Gregghz.DisplayManager.Windows.ViewModels;
-
-public class SaveLayoutCommand(IDisplayService displayService, ILayoutService layoutService) : ICommand
-{
- public bool CanExecute(object? parameter)
- {
- return true;
- }
-
- public async void Execute(object? parameter)
- {
- if (parameter is not Func getter) return;
-
- var value = getter();
- var currentLayout = displayService.GetDisplayLayout();
- await layoutService.SaveLayout(value, currentLayout);
- LayoutSaved?.Invoke(this, EventArgs.Empty);
- }
-
- public event EventHandler? CanExecuteChanged;
- public event EventHandler? LayoutSaved;
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml b/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml
deleted file mode 100644
index 038198b..0000000
--- a/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml.cs b/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml.cs
deleted file mode 100644
index f6c5723..0000000
--- a/DisplayManager.Windows/Views/Dialogs/SaveDialogContent.xaml.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Gregghz.DisplayManager.Windows.ViewModels;
-
-namespace Gregghz.DisplayManager.Windows.Views.Dialogs;
-
-public sealed partial class SaveDialogContent
-{
- public SaveDialogContent()
- {
- InitializeComponent();
- ViewModel = new SaveDialogViewModel(PreviewCanvas);
- DataContext = ViewModel;
- }
-
- public SaveDialogViewModel ViewModel { get; set; }
-}
\ No newline at end of file
diff --git a/DisplayManager.Windows/Views/MainWindow.xaml b/DisplayManager.Windows/Views/MainWindow.xaml
deleted file mode 100644
index 98e01e2..0000000
--- a/DisplayManager.Windows/Views/MainWindow.xaml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/DisplayManager.Windows/Views/MainWindow.xaml.cs b/DisplayManager.Windows/Views/MainWindow.xaml.cs
deleted file mode 100644
index 323299a..0000000
--- a/DisplayManager.Windows/Views/MainWindow.xaml.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using Windows.Graphics;
-using Gregghz.DisplayManager.Model;
-using Gregghz.DisplayManager.Services;
-using Gregghz.DisplayManager.Windows.ViewModels;
-using Gregghz.DisplayManager.Windows.Views.Dialogs;
-using Microsoft.UI;
-using Microsoft.UI.Windowing;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Media;
-using Microsoft.UI.Xaml.Shapes;
-using WinRT.Interop;
-using Layout = Gregghz.DisplayManager.Model.Layout;
-using Size = Windows.Foundation.Size;
-
-namespace Gregghz.DisplayManager.Windows.Views;
-
-///
-/// Interaction logic for MainWindow.xaml
-///
-public partial class MainWindow : Window
-{
- private readonly Dictionary _deviceMap;
- private readonly IDisplayService _displayService;
- private readonly ILayoutService _layoutService;
-
- private bool _firstLoad = true;
-
- public MainWindow(IDisplayService displayService, ILayoutService layoutService)
- {
- _displayService = displayService;
- _layoutService = layoutService;
- InitializeComponent();
-
- ViewModel = new MainWindowViewModel(displayService, layoutService);
- _deviceMap = displayService.GetDeviceMap();
- var appWindow = GetAppWindowForCurrentWindow();
- appWindow.Resize(new SizeInt32(825, 600));
-
- // _displayManager.LayoutsUpdated += (_, layouts) => { RefreshLayouts(layouts); };
- // _displayManager.LayoutLoaded += OnLayoutLoad;
-
- ViewModel.SubscribeToSaveDialogRequested(DrawSaveDialog);
- ViewModel.SubscribeToLayoutSelected(LayoutLoaded);
- ViewModel.SubscribeToLayoutSaved(RefreshLayouts);
- ViewModel.SubscribeToLayoutCountChanged(count =>
- {
- if (count > 0)
- {
- LayoutsListBox.IsEnabled = true;
- LayoutsTextBlock.Visibility = Visibility.Collapsed;
- }
- else
- {
- LayoutsListBox.IsEnabled = false;
- LayoutsTextBlock.Visibility = Visibility.Visible;
- }
- });
- }
-
- public MainWindowViewModel ViewModel { get; set; }
-
- private AppWindow GetAppWindowForCurrentWindow()
- {
- var hWnd = WindowNative.GetWindowHandle(this);
- var windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
- return AppWindow.GetFromWindowId(windowId);
- }
-
- private async void Window_Activated(object sender, WindowActivatedEventArgs e)
- {
- if (_firstLoad) _firstLoad = false;
- // _displayManager.FireLayoutUpdated();
- }
-
- private void RefreshLayouts(IList layouts)
- {
- ViewModel.Layouts.Clear();
-
- foreach (var layout in layouts)
- ViewModel.Layouts.Add(layout);
- }
-
- private void LayoutLoaded(Layout layout)
- {
- DrawLayout(layout.Settings, LayoutCanvas);
- }
-
- private void DrawLayout(IList layout, Canvas canvas, double? scale = null)
- {
- canvas.Children.Clear();
-
- var canvasHeight = canvas.ActualHeight;
- var canvasWidth = canvas.ActualWidth;
- if (canvasHeight == 0 || canvasWidth == 0)
- {
- canvas.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
- canvasHeight = canvas.DesiredSize.Height;
- canvasWidth = canvas.DesiredSize.Width;
- }
-
- var scalingFactor = 96.0 / 2;
- if (scale is not null) scalingFactor = scale.Value;
-
- var leftMost = layout.MinBy(s => s.Position.X);
- var rightMost = layout.MaxBy(s => s.RightX);
-
- var topMost = layout.MinBy(s => s.Position.Y);
- var bottomMost = layout.MaxBy(s => s.BottomY);
-
- var layoutWidth = 0.0;
- var layoutHeight = 0.0;
- if (leftMost is not null && rightMost is not null && topMost is not null && bottomMost is not null)
- {
- layoutWidth = (rightMost.RightX - leftMost.Position.X) / scalingFactor;
- layoutHeight = (bottomMost.BottomY - topMost.Position.Y) / scalingFactor;
- }
-
- foreach (var settings in layout)
- {
- var offsetX = canvasWidth / 2 - layoutWidth / 2;
- var offsetY = canvasHeight / 2 - layoutHeight / 2;
- var monitorShapes = DrawMonitor(settings, offsetX, offsetY, scalingFactor);
- foreach (var shape in monitorShapes) canvas.Children.Add(shape);
- }
- }
-
- private FrameworkElement[] DrawMonitor(Settings settings, double offsetX, double offsetY, double scalingFactor)
- {
- var strokeColor = settings.IsPrimary ? Colors.Indigo : Colors.Black;
- var rect = new Rectangle
- {
- Stroke = new SolidColorBrush(strokeColor),
- Fill = new SolidColorBrush(Colors.Transparent),
- StrokeThickness = 2,
- Width = settings.Resolution.Width / scalingFactor,
- Height = settings.Resolution.Height / scalingFactor
- };
- var toolTip = new ToolTip
- {
- Content = _deviceMap[settings.DeviceId]
- };
- ToolTipService.SetToolTip(rect, toolTip);
-
- var rectLeft = settings.Position.X / scalingFactor + offsetX;
- var rectTop = settings.Position.Y / scalingFactor + offsetY;
-
- Canvas.SetLeft(rect, rectLeft);
- Canvas.SetTop(rect, rectTop);
-
- var resolutionTextBlock = DrawResolutionTextBlock(settings, scalingFactor);
- var resolutionTextBlockSize = resolutionTextBlock.DesiredSize;
-
- // Get the center position of the rectangle.
- var rectCenterX = rectLeft + rect.Width / 2;
- var rectCenterY = rectTop + rect.Height / 2;
-
- // Calculate the top-left position for the TextBox to make it centered within the Rectangle.
- var textBoxLeft = rectCenterX - resolutionTextBlockSize.Width / 2;
- var textBoxTop = rectCenterY - resolutionTextBlockSize.Height / 2;
-
- // Apply the calculated position to the TextBox.
- Canvas.SetLeft(resolutionTextBlock, textBoxLeft);
- Canvas.SetTop(resolutionTextBlock, textBoxTop);
-
- return [rect, resolutionTextBlock];
- }
-
- private static TextBlock DrawResolutionTextBlock(Settings settings, double scalingFactor, double fontSize = 9)
- {
- var resolutionTextBlock = new TextBlock
- {
- Text = $"{settings.Resolution.Width}x{settings.Resolution.Height}",
- FontSize = fontSize
- };
- resolutionTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
- var resolutionTextBlockSize = resolutionTextBlock.DesiredSize;
- if (fontSize > 1 && resolutionTextBlockSize.Width >= settings.Resolution.Width / scalingFactor - 5)
- return DrawResolutionTextBlock(settings, scalingFactor, fontSize - 1);
-
- return resolutionTextBlock;
- }
-
- private async void DrawSaveDialog(object? sender, Layout currentLayout)
- {
- var content = new SaveDialogContent();
- var cd = new ContentDialog
- {
- XamlRoot = RootGrid.XamlRoot,
- Width = 300,
- Height = 500,
- Content = content,
- CloseButtonText = "Cancel",
- PrimaryButtonText = "Save",
- PrimaryButtonCommand = ViewModel.SaveLayoutCommand,
- PrimaryButtonCommandParameter = () => content.ViewModel.LayoutName
- };
- DrawLayout(currentLayout.Settings, content.ViewModel.Canvas, 96.0 / 1.5);
- await cd.ShowAsync();
- }
-}
\ No newline at end of file
diff --git a/DisplayManager/Ui/Cli/ConsoleMain.cs b/DisplayManager/Cli/ConsoleMain.cs
similarity index 59%
rename from DisplayManager/Ui/Cli/ConsoleMain.cs
rename to DisplayManager/Cli/ConsoleMain.cs
index fcc4fd9..1513827 100644
--- a/DisplayManager/Ui/Cli/ConsoleMain.cs
+++ b/DisplayManager/Cli/ConsoleMain.cs
@@ -1,22 +1,22 @@
using CommandLine;
using Gregghz.DisplayManager.Services;
-using Gregghz.DisplayManager.UI.Cli.Model;
+using Gregghz.DisplayManager.Cli.Model;
-namespace Gregghz.DisplayManager.UI.Cli;
+namespace Gregghz.DisplayManager.Cli;
public class ConsoleMain(IDisplayService displayService, ILayoutService layoutService)
{
public void Run(string[] args)
{
Parser.Default.ParseArguments(args)
- .WithParsed(Run)
+ .WithParsed(opts => Run(opts).GetAwaiter().GetResult())
.WithNotParsed(errs =>
{
foreach (var err in errs) Console.WriteLine(err);
});
}
- private async void Run(CliOptions opts)
+ private async Task Run(CliOptions opts)
{
if (opts.Info)
{
@@ -31,6 +31,12 @@ private async void Run(CliOptions opts)
return;
}
+ if (opts.ClearLayouts)
+ {
+ await ClearLayouts();
+ return;
+ }
+
switch (opts.ConfigToLoad, opts.ConfigToSave)
{
case (not null, null):
@@ -56,6 +62,31 @@ private async Task ListLayouts()
{
var layoutNames = await layoutService.GetSavedLayouts();
+ if (layoutNames.Count == 0)
+ {
+ var previousColor = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine("No saved layouts found.");
+ Console.ForegroundColor = previousColor;
+ return;
+ }
+
foreach (var layoutName in layoutNames) Console.WriteLine(layoutName);
}
+
+ private async Task ClearLayouts()
+ {
+ var clearedCount = await layoutService.ClearLayouts();
+
+ if (clearedCount == 0)
+ {
+ var previousColor = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine("No saved layouts found.");
+ Console.ForegroundColor = previousColor;
+ return;
+ }
+
+ Console.WriteLine($"Cleared {clearedCount} saved layout(s).");
+ }
}
\ No newline at end of file
diff --git a/DisplayManager/Ui/Cli/Model/CliOptions.cs b/DisplayManager/Cli/Model/CliOptions.cs
similarity index 77%
rename from DisplayManager/Ui/Cli/Model/CliOptions.cs
rename to DisplayManager/Cli/Model/CliOptions.cs
index 7a3ca6c..fc7524f 100644
--- a/DisplayManager/Ui/Cli/Model/CliOptions.cs
+++ b/DisplayManager/Cli/Model/CliOptions.cs
@@ -1,6 +1,6 @@
using CommandLine;
-namespace Gregghz.DisplayManager.UI.Cli.Model;
+namespace Gregghz.DisplayManager.Cli.Model;
public class CliOptions
{
@@ -15,4 +15,7 @@ public class CliOptions
[Option('l', "list", Required = false, HelpText = "List all current saved layouts.")]
public bool ListLayouts { get; set; } = false;
+
+ [Option('c', "clear", Required = false, HelpText = "Delete all saved layouts.")]
+ public bool ClearLayouts { get; set; } = false;
}
\ No newline at end of file
diff --git a/DisplayManager/Model/Settings.cs b/DisplayManager/Model/Settings.cs
index 9584f76..679c97c 100644
--- a/DisplayManager/Model/Settings.cs
+++ b/DisplayManager/Model/Settings.cs
@@ -3,6 +3,7 @@ namespace Gregghz.DisplayManager.Model;
public record Settings(
string DeviceId,
bool IsPrimary,
+ bool IsConnected,
Position Position,
Resolution Resolution,
Orientation Orientation,
@@ -13,6 +14,7 @@ uint Frequency
public int BottomY => Position.Y + Resolution.Height;
public string DeviceId { get; set; } = DeviceId;
public bool IsPrimary { get; set; } = IsPrimary;
+ public bool IsConnected { get; set; } = IsConnected;
public Position Position { get; set; } = Position;
public Resolution Resolution { get; set; } = Resolution;
public Orientation Orientation { get; set; } = Orientation;
diff --git a/DisplayManager/Services/ILayoutService.cs b/DisplayManager/Services/ILayoutService.cs
index 51b5b92..6f2a566 100644
--- a/DisplayManager/Services/ILayoutService.cs
+++ b/DisplayManager/Services/ILayoutService.cs
@@ -8,6 +8,7 @@ public interface ILayoutService
event EventHandler>? LayoutsChanged;
Task> GetSavedLayouts();
+ Task ClearLayouts();
Task GetLayout(string name);
Task SaveLayout(string name, Layout layout);
}
\ No newline at end of file
diff --git a/DisplayManager/Services/Implementations/FileSystemLayoutService.cs b/DisplayManager/Services/Implementations/FileSystemLayoutService.cs
index bcfe861..20b4afb 100644
--- a/DisplayManager/Services/Implementations/FileSystemLayoutService.cs
+++ b/DisplayManager/Services/Implementations/FileSystemLayoutService.cs
@@ -21,6 +21,16 @@ public Task> GetSavedLayouts()
});
}
+ public Task ClearLayouts()
+ {
+ return Task.Run(() =>
+ {
+ var savedLayouts = Directory.GetFiles(basePath, "*.json");
+ foreach (var layoutPath in savedLayouts) File.Delete(layoutPath);
+ return savedLayouts.Length;
+ });
+ }
+
public async Task GetLayout(string name)
{
var configPath = Path.Combine(basePath, $"{name}.json");
diff --git a/Taskfile.yml b/Taskfile.yml
new file mode 100644
index 0000000..447d79e
--- /dev/null
+++ b/Taskfile.yml
@@ -0,0 +1,142 @@
+version: '3'
+
+vars:
+ SOLUTION: DisplayManager.sln
+ CORE_LIBRARY_PROJECT: DisplayManager/DisplayManager.csproj
+ WINDOWS_HOST_PROJECT: '{{.TASKFILE_DIR}}\DisplayManager.Windows\DisplayManager.Windows.csproj'
+ WINDOWS_HOST_EXE: '{{.TASKFILE_DIR}}\DisplayManager.Windows\bin\Debug\net8.0-windows10.0.19041.0\DisplayManager.Windows.exe'
+ LINUX_HOST_PROJECT: DisplayManager.Linux/DisplayManager.Linux.csproj
+ MAC_HOST_PROJECT: DisplayManager.Mac/DispayManager.Mac.csproj
+
+tasks:
+ default:
+ desc: Show available tasks
+ cmds:
+ - task --list-all
+
+ restore:
+ desc: Restore NuGet packages
+ sources:
+ - DisplayManager.sln
+ - global.json
+ - DisplayManager/DisplayManager.csproj
+ - DisplayManager.Windows/DisplayManager.Windows.csproj
+ - DisplayManager.Linux/DisplayManager.Linux.csproj
+ - DisplayManager.Mac/DispayManager.Mac.csproj
+ - Directory.Packages.props
+ - Directory.Build.props
+ - Directory.Build.targets
+ - nuget.config
+ - NuGet.config
+ generates:
+ - DisplayManager/obj/project.assets.json
+ - DisplayManager.Windows/obj/project.assets.json
+ - DisplayManager.Linux/obj/project.assets.json
+ - DisplayManager.Mac/obj/project.assets.json
+ cmds:
+ - dotnet restore {{.SOLUTION}}
+
+ build:
+ desc: Build all projects in the solution (Debug)
+ deps: [restore]
+ sources:
+ - DisplayManager.sln
+ - global.json
+ - DisplayManager/**/*.cs
+ - DisplayManager/DisplayManager.csproj
+ - DisplayManager.Windows/**/*.cs
+ - DisplayManager.Windows/DisplayManager.Windows.csproj
+ - DisplayManager.Linux/**/*.cs
+ - DisplayManager.Linux/DisplayManager.Linux.csproj
+ - DisplayManager.Mac/**/*.cs
+ - DisplayManager.Mac/DispayManager.Mac.csproj
+ generates:
+ - DisplayManager/bin/Debug/net8.0/DisplayManager.dll
+ - DisplayManager.Windows/bin/Debug/net8.0-windows10.0.19041.0/DisplayManager.Windows.dll
+ - DisplayManager.Linux/bin/Debug/net8.0/DisplayManager.Linux.dll
+ - DisplayManager.Mac/bin/Debug/net8.0/DispayManager.Mac.dll
+ cmds:
+ - dotnet build {{.SOLUTION}} --configuration Debug --no-restore
+
+ build-release:
+ desc: Build all projects in the solution (Release)
+ deps: [restore]
+ cmds:
+ - dotnet build {{.SOLUTION}} --configuration Release --no-restore
+
+ clean:
+ desc: Clean build outputs
+ cmds:
+ - dotnet clean {{.SOLUTION}}
+
+ test:
+ desc: Run tests for the solution
+ deps: [build]
+ cmds:
+ - dotnet test {{.SOLUTION}} --configuration Debug --no-build --verbosity minimal
+
+ run:
+ desc: "Run the Windows host executable that uses the shared DisplayManager library"
+ deps: [build]
+ platforms: [windows]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" {{.CLI_ARGS}}'
+
+ run-linux:
+ desc: "Run the Linux host executable that uses the shared DisplayManager library"
+ deps: [build]
+ platforms: [linux]
+ cmds:
+ - dotnet run --project {{.LINUX_HOST_PROJECT}} --configuration Debug --no-build -- {{.CLI_ARGS}}
+
+ run-mac:
+ desc: "Run the Mac host executable that uses the shared DisplayManager library"
+ deps: [build]
+ platforms: [darwin]
+ cmds:
+ - dotnet run --project {{.MAC_HOST_PROJECT}} --configuration Debug --no-build -- {{.CLI_ARGS}}
+
+ save:
+ desc: "Save current display layout via the Windows host"
+ deps: [build]
+ platforms: [windows]
+ requires:
+ vars: [NAME]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" --save {{.NAME}}'
+
+ load:
+ desc: "Load a saved display layout via the Windows host"
+ deps: [build]
+ platforms: [windows]
+ requires:
+ vars: [NAME]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" --load {{.NAME}}'
+
+ list-layouts:
+ desc: "List saved display layouts via the Windows host"
+ deps: [build]
+ platforms: [windows]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" --list'
+
+ info:
+ desc: "Show display information via the Windows host"
+ deps: [build]
+ platforms: [windows]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" --info'
+
+ clear-layouts:
+ desc: "Clear all saved display layouts via the Windows host"
+ deps: [build]
+ platforms: [windows]
+ cmds:
+ - '"{{.WINDOWS_HOST_EXE}}" --clear'
+
+ build-core:
+ desc: Build only the shared DisplayManager library (Debug)
+ deps: [restore]
+ cmds:
+ - dotnet build {{.CORE_LIBRARY_PROJECT}} --configuration Debug --no-restore