diff --git a/NightGlow.MonitorConfig/Monitors.cs b/NightGlow.MonitorConfig/Monitors.cs new file mode 100644 index 0000000..ceaae7a --- /dev/null +++ b/NightGlow.MonitorConfig/Monitors.cs @@ -0,0 +1,21 @@ +namespace NightGlow.MonitorConfig; + +public class Monitors +{ + + public List VirtualMonitors = new List(); + + public bool Scan() + { + VirtualMonitors.Clear(); + bool success = WinApi.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumCallback, IntPtr.Zero); + return success; + } + + private bool MonitorEnumCallback(IntPtr hMonitor, IntPtr hdcMonitor, ref WinApi.RECT lprcMonitor, IntPtr dwData) + { + VirtualMonitors.Add(new VirtualMonitor(hMonitor, VirtualMonitors.Count)); + return true; + } + +} diff --git a/NightGlow.MonitorConfig/NightGlow.MonitorConfig.csproj b/NightGlow.MonitorConfig/NightGlow.MonitorConfig.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/NightGlow.MonitorConfig/NightGlow.MonitorConfig.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/NightGlow.MonitorConfig/PhysicalMonitor.cs b/NightGlow.MonitorConfig/PhysicalMonitor.cs new file mode 100644 index 0000000..7cbe931 --- /dev/null +++ b/NightGlow.MonitorConfig/PhysicalMonitor.cs @@ -0,0 +1,45 @@ +using static NightGlow.MonitorConfig.WinApi; + +namespace NightGlow.MonitorConfig; + +public class PhysicalMonitor +{ + + PHYSICAL_MONITOR Monitor; + + public string Description; + + Setting Brightness = new(); + Setting Contrast = new(); + + public PhysicalMonitor(PHYSICAL_MONITOR monitor) + { + Monitor = monitor; + Description = new string(Monitor.szPhysicalMonitorDescription); + } + + public Setting GetBrightness() + { + bool success = GetMonitorBrightness(Monitor.hPhysicalMonitor, out Brightness.Min, out Brightness.Current, out Brightness.Max); + return Brightness; + } + + public void SetBrightness(uint value) + { + bool success = SetMonitorBrightness(Monitor.hPhysicalMonitor, value); + Brightness.Current = value; + } + + public Setting GetContrast() + { + bool success = GetMonitorContrast(Monitor.hPhysicalMonitor, out Contrast.Min, out Contrast.Current, out Contrast.Max); + return Contrast; + } + + public void SetContrast(uint value) + { + bool success = SetMonitorContrast(Monitor.hPhysicalMonitor, value); + Contrast.Current = value; + } + +} diff --git a/NightGlow.MonitorConfig/Setting.cs b/NightGlow.MonitorConfig/Setting.cs new file mode 100644 index 0000000..c44ff25 --- /dev/null +++ b/NightGlow.MonitorConfig/Setting.cs @@ -0,0 +1,24 @@ +namespace NightGlow.MonitorConfig; + +public class Setting +{ + + public uint Min; + public uint Max; + public uint Current; + + public Setting() + { + Min = 0; + Max = 0; + Current = 0; + } + + public Setting(uint min, uint max, uint current) + { + Min = min; + Max = max; + Current = current; + } + +} diff --git a/NightGlow.MonitorConfig/VirtualMonitor.cs b/NightGlow.MonitorConfig/VirtualMonitor.cs new file mode 100644 index 0000000..2b40344 --- /dev/null +++ b/NightGlow.MonitorConfig/VirtualMonitor.cs @@ -0,0 +1,106 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using static NightGlow.MonitorConfig.WinApi; + +namespace NightGlow.MonitorConfig; + +public class VirtualMonitor +{ + + public string DeviceName = ""; + public string FriendlyName = ""; + + public List PhysicalMonitors = new(); + + private MONITORINFOEX MonitorInfo; + + public VirtualMonitor(IntPtr hMonitor, int index) + { + MonitorInfo = new MONITORINFOEX { cbSize = (uint)Marshal.SizeOf() }; + + if (!GetMonitorInfo(hMonitor, ref MonitorInfo)) + { + // TODO throw error or log + Debug.WriteLine("Error: GetMonitorInfo"); + } + + DeviceName = MonitorInfo.szDevice; + + LoadFriendlyName(index); + + GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, out uint physicalMonitorCount); + if (physicalMonitorCount == 0) + return; + + PHYSICAL_MONITOR[] physicalMonitorArray = new PHYSICAL_MONITOR[physicalMonitorCount]; + GetPhysicalMonitorsFromHMONITOR(hMonitor, physicalMonitorCount, physicalMonitorArray); + + for (int i = 0; i < physicalMonitorCount; i++) + { + PhysicalMonitors.Add(new PhysicalMonitor(physicalMonitorArray[i])); + } + } + + public bool IsPrimary() + { + return (MonitorInfo.dwFlags & MONITORINFOF.MONITORINFOF_PRIMARY) == MONITORINFOF.MONITORINFOF_PRIMARY; + } + + private void LoadFriendlyName(int index) + { + FriendlyName = ""; + + uint pathCount = 0, modeCount = 0; + + long error = GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, + ref pathCount, ref modeCount); + if (error != ERROR_SUCCESS) + { + // TODO throw error or log + Debug.WriteLine("Error: GetDisplayConfigBufferSizes"); + return; + } + + DISPLAYCONFIG_PATH_INFO[] displayPaths = new DISPLAYCONFIG_PATH_INFO[pathCount]; + DISPLAYCONFIG_MODE_INFO[] displayModes = new DISPLAYCONFIG_MODE_INFO[modeCount]; + + error = QueryDisplayConfig(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, + ref pathCount, displayPaths, ref modeCount, displayModes, IntPtr.Zero); + if (error != ERROR_SUCCESS) + { + // TODO throw error or log + Debug.WriteLine("Error: QueryDisplayConfig"); + return; + } + + int modeTargetCount = 0; + for (int i = 0; i < modeCount; i++) + { + if (displayModes[i].infoType != DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) + continue; + + if (modeTargetCount != index) + { + modeTargetCount++; + continue; + } + + DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME(); + deviceName.header.size = (uint)Marshal.SizeOf(typeof(DISPLAYCONFIG_TARGET_DEVICE_NAME)); + deviceName.header.adapterId = displayModes[i].adapterId; + deviceName.header.id = displayModes[i].id; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + + error = DisplayConfigGetDeviceInfo(ref deviceName); + if (error != ERROR_SUCCESS) + { + // TODO throw error or log + Debug.WriteLine("Error: DisplayConfigGetDeviceInfo"); + return; + } + FriendlyName = deviceName.monitorFriendlyDeviceName; + return; + } + } + +} diff --git a/NightGlow.MonitorConfig/WinApi.cs b/NightGlow.MonitorConfig/WinApi.cs new file mode 100644 index 0000000..570944b --- /dev/null +++ b/NightGlow.MonitorConfig/WinApi.cs @@ -0,0 +1,318 @@ +using System.Runtime.InteropServices; + +namespace NightGlow.MonitorConfig; + +public class WinApi +{ + + public const int ERROR_SUCCESS = 0; + + [DllImport("user32.dll")] + public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData); + + public delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData); + + [DllImport("User32.dll", EntryPoint = "GetMonitorInfoW")] + public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); + + [DllImport("User32.dll")] + public static extern int GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS Flags, ref uint numPathArrayElements, ref uint numModeInfoArrayElements); + + [DllImport("user32.dll")] + public static extern int QueryDisplayConfig( + QUERY_DEVICE_CONFIG_FLAGS Flags, + ref uint NumPathArrayElements, + [Out] DISPLAYCONFIG_PATH_INFO[] PathInfoArray, + ref uint NumModeInfoArrayElements, + [Out] DISPLAYCONFIG_MODE_INFO[] ModeInfoArray, + IntPtr CurrentTopologyId + ); + + [DllImport("user32.dll")] + public static extern int DisplayConfigGetDeviceInfo( + ref DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName + ); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool GetNumberOfPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, out uint pdwNumberOfPhysicalMonitors); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool GetPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, uint dwPhysicalMonitorArraySize, [Out] PHYSICAL_MONITOR[] pPhysicalMonitorArray); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool GetMonitorCapabilities(IntPtr hMonitor, out uint pdwMonitorCapabilities, out uint pdwSupportedColorTemperatures); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool GetMonitorBrightness(IntPtr hMonitor, out uint pdwMinimumBrightness, out uint pdwCurrentBrightness, out uint pdwMaximumBrightness); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool SetMonitorBrightness(IntPtr hMonitor, uint dwNewBrightness); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool GetMonitorContrast(IntPtr hMonitor, out uint pdwMinimumContrast, out uint pdwCurrentContrast, out uint pdwMaximumContrast); + + [DllImport("dxva2.dll", SetLastError = true)] + public extern static bool SetMonitorContrast(IntPtr hMonitor, uint dwNewContrast); + + + private const int PHYSICAL_MONITOR_DESCRIPTION_SIZE = 128; + + [StructLayout(LayoutKind.Sequential)] + public struct PHYSICAL_MONITOR + { + public IntPtr hPhysicalMonitor; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U2, SizeConst = PHYSICAL_MONITOR_DESCRIPTION_SIZE)] + public char[] szPhysicalMonitorDescription; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DISPLAYCONFIG_TARGET_DEVICE_NAME + { + public DISPLAYCONFIG_DEVICE_INFO_HEADER header; + public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags; + public DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; + public ushort edidManufactureId; + public ushort edidProductCodeId; + public uint connectorInstance; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string monitorFriendlyDeviceName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string monitorDevicePath; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_DEVICE_INFO_HEADER + { + public DISPLAYCONFIG_DEVICE_INFO_TYPE type; + public uint size; + public LUID adapterId; + public uint id; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS + { + public uint value; + } + public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint + { + DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, + DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, + DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, + DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF + } + + + + public enum QUERY_DEVICE_CONFIG_FLAGS : uint + { + QDC_ALL_PATHS = 0x00000001, + QDC_ONLY_ACTIVE_PATHS = 0x00000002, + QDC_DATABASE_CURRENT = 0x00000004 + } + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT { public int left, top, right, bottom; } + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct MONITORINFOEX + { + public uint cbSize; + public RECT rcMonitor; + public RECT rcWork; + public MONITORINFOF dwFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string szDevice; + } + + public enum MONITORINFOF : uint + { + MONITORINFOF_NONE = 0x00000000, + MONITORINFOF_PRIMARY = 0x00000001, + } + + + + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_INFO + { + public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; + public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; + public uint flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_MODE_INFO + { + public DISPLAYCONFIG_MODE_INFO_TYPE infoType; + public uint id; + public LUID adapterId; + public DISPLAYCONFIG_MODE_INFO_UNION modeInfo; + } + + [StructLayout(LayoutKind.Explicit)] + public struct DISPLAYCONFIG_MODE_INFO_UNION + { + [FieldOffset(0)] + public DISPLAYCONFIG_TARGET_MODE targetMode; + [FieldOffset(0)] + public DISPLAYCONFIG_SOURCE_MODE sourceMode; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_TARGET_MODE + { + public DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO + { + public ulong pixelRate; + public DISPLAYCONFIG_RATIONAL hSyncFreq; + public DISPLAYCONFIG_RATIONAL vSyncFreq; + public DISPLAYCONFIG_2DREGION activeSize; + public DISPLAYCONFIG_2DREGION totalSize; + public uint videoStandard; + public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_2DREGION + { + public uint cx; + public uint cy; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_SOURCE_MODE + { + public uint width; + public uint height; + public DISPLAYCONFIG_PIXELFORMAT pixelFormat; + public POINTL position; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINTL + { + int x; + int y; + } + + public enum DISPLAYCONFIG_PIXELFORMAT : uint + { + DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, + DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, + DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, + DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, + DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5, + DISPLAYCONFIG_PIXELFORMAT_FORCE_UINT32 = 0xffffffff + } + + public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint + { + DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, + DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2, + DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF + } + + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public uint LowPart; + public int HighPart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_SOURCE_INFO + { + public LUID adapterId; + public uint id; + public uint modeInfoIdx; + public uint statusFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_TARGET_INFO + { + public LUID adapterId; + public uint id; + public uint modeInfoIdx; + DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; + DISPLAYCONFIG_ROTATION rotation; + DISPLAYCONFIG_SCALING scaling; + DISPLAYCONFIG_RATIONAL refreshRate; + DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; + public bool targetAvailable; + public uint statusFlags; + } + + public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint + { + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = 0xFFFFFFFF, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = 1, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = 2, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = 3, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = 4, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = 5, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = 6, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = 8, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = 9, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = 10, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = 11, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = 12, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = 13, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = 14, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST = 15, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = 0x80000000, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_ROTATION : uint + { + DISPLAYCONFIG_ROTATION_IDENTITY = 1, + DISPLAYCONFIG_ROTATION_ROTATE90 = 2, + DISPLAYCONFIG_ROTATION_ROTATE180 = 3, + DISPLAYCONFIG_ROTATION_ROTATE270 = 4, + DISPLAYCONFIG_ROTATION_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_SCALING : uint + { + DISPLAYCONFIG_SCALING_IDENTITY = 1, + DISPLAYCONFIG_SCALING_CENTERED = 2, + DISPLAYCONFIG_SCALING_STRETCHED = 3, + DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, + DISPLAYCONFIG_SCALING_CUSTOM = 5, + DISPLAYCONFIG_SCALING_PREFERRED = 128, + DISPLAYCONFIG_SCALING_FORCE_UINT32 = 0xFFFFFFFF + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_RATIONAL + { + public uint Numerator; + public uint Denominator; + } + + public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint + { + DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, + DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED = 2, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST = 3, + DISPLAYCONFIG_SCANLINE_ORDERING_FORCE_UINT32 = 0xFFFFFFFF + } + + +} diff --git a/NightGlow.WindowsApi/DeviceContext.cs b/NightGlow.WindowsApi/DeviceContext.cs index a542209..bb492ee 100644 --- a/NightGlow.WindowsApi/DeviceContext.cs +++ b/NightGlow.WindowsApi/DeviceContext.cs @@ -5,11 +5,14 @@ namespace NightGlow.WindowsApi; public partial class DeviceContext : NativeResource { + private int _gammaChannelOffset; - public DeviceContext(nint handle) - : base(handle) + public string DeviceName; + + public DeviceContext(nint handle, string deviceName) : base(handle) { + DeviceName = deviceName; } private void SetGammaRamp(GammaRamp ramp) @@ -71,7 +74,7 @@ public partial class DeviceContext return null; } - return new DeviceContext(handle); + return new DeviceContext(handle, deviceName); } public static IReadOnlyList GetAllScreens() diff --git a/NightGlow.sln b/NightGlow.sln index a145663..e8431ba 100644 --- a/NightGlow.sln +++ b/NightGlow.sln @@ -4,23 +4,60 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.7.34009.444 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NightGlow", "NightGlow\NightGlow.csproj", "{8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}" + ProjectSection(ProjectDependencies) = postProject + {5D7C7E03-E446-49ED-977A-2EA6824FE29A} = {5D7C7E03-E446-49ED-977A-2EA6824FE29A} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NightGlow.WindowsApi", "NightGlow.WindowsApi\NightGlow.WindowsApi.csproj", "{961F5C16-4446-40EC-9A72-1C352920335D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NightGlow.WindowsApi", "NightGlow.WindowsApi\NightGlow.WindowsApi.csproj", "{961F5C16-4446-40EC-9A72-1C352920335D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NightGlow.MonitorConfig", "NightGlow.MonitorConfig\NightGlow.MonitorConfig.csproj", "{5D7C7E03-E446-49ED-977A-2EA6824FE29A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|x64.Build.0 = Debug|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Debug|x86.Build.0 = Debug|Any CPU {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|Any CPU.Build.0 = Release|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|x64.ActiveCfg = Release|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|x64.Build.0 = Release|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|x86.ActiveCfg = Release|Any CPU + {8E4BBB59-4D6D-48CF-9E04-CB3D7CA97401}.Release|x86.Build.0 = Release|Any CPU {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|x64.ActiveCfg = Debug|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|x64.Build.0 = Debug|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|x86.ActiveCfg = Debug|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Debug|x86.Build.0 = Debug|Any CPU {961F5C16-4446-40EC-9A72-1C352920335D}.Release|Any CPU.ActiveCfg = Release|Any CPU {961F5C16-4446-40EC-9A72-1C352920335D}.Release|Any CPU.Build.0 = Release|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Release|x64.ActiveCfg = Release|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Release|x64.Build.0 = Release|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Release|x86.ActiveCfg = Release|Any CPU + {961F5C16-4446-40EC-9A72-1C352920335D}.Release|x86.Build.0 = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|x64.Build.0 = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Debug|x86.Build.0 = Debug|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|Any CPU.Build.0 = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|x64.ActiveCfg = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|x64.Build.0 = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|x86.ActiveCfg = Release|Any CPU + {5D7C7E03-E446-49ED-977A-2EA6824FE29A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NightGlow/App.config b/NightGlow/App.config index d79560c..a2985f4 100644 --- a/NightGlow/App.config +++ b/NightGlow/App.config @@ -49,6 +49,9 @@ + + + \ No newline at end of file diff --git a/NightGlow/App.xaml b/NightGlow/App.xaml index 8ba8366..7587f70 100644 --- a/NightGlow/App.xaml +++ b/NightGlow/App.xaml @@ -36,7 +36,7 @@ @@ -46,7 +46,7 @@ - 12 + 10 diff --git a/NightGlow/App.xaml.cs b/NightGlow/App.xaml.cs index 915dff1..2e4a6d8 100644 --- a/NightGlow/App.xaml.cs +++ b/NightGlow/App.xaml.cs @@ -41,6 +41,7 @@ private void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/NightGlow/Data/DdcConfig.cs b/NightGlow/Data/DdcConfig.cs new file mode 100644 index 0000000..eaadd52 --- /dev/null +++ b/NightGlow/Data/DdcConfig.cs @@ -0,0 +1,66 @@ +using NightGlow.MonitorConfig; +using System; +using System.Collections.Generic; + +namespace NightGlow.Data; + +[Serializable] +public class DdcConfig +{ + + public IList DdcMonitorItems; + + public DdcConfig() + { + DdcMonitorItems = new List(); + } + + public DdcMonitorItem GetOrCreateDdcMonitorItem(VirtualMonitor vm) + { + foreach (var item in DdcMonitorItems) + if (item.Name.Equals(vm.FriendlyName) && item.DeviceName.Equals(vm.DeviceName)) + return item; + + DdcMonitorItem newItem = new DdcMonitorItem + { + Name = vm.FriendlyName, + DeviceName = vm.DeviceName, + EnableDdc = false, + MinBrightnessPct = 0, + MaxBrightness = 100 + }; + DdcMonitorItems.Add(newItem); + return newItem; + } + + public void SetEnableDdc(VirtualMonitor vm, bool value) + { + DdcMonitorItem item = GetOrCreateDdcMonitorItem(vm); + item.EnableDdc = value; + } + + public void SetMinBrightnessPct(VirtualMonitor vm, int value) + { + DdcMonitorItem item = GetOrCreateDdcMonitorItem(vm); + item.MinBrightnessPct = value; + } + + public void SetMaxBrightness(VirtualMonitor vm, int value) + { + DdcMonitorItem item = GetOrCreateDdcMonitorItem(vm); + item.MaxBrightness = value; + } + +} + +[Serializable] +public class DdcMonitorItem +{ + public string Name; + public string DeviceName; + + public bool EnableDdc; + + public int MinBrightnessPct; + public int MaxBrightness; +} diff --git a/NightGlow/NightGlow.csproj b/NightGlow/NightGlow.csproj index 555ffbb..62ece37 100644 --- a/NightGlow/NightGlow.csproj +++ b/NightGlow/NightGlow.csproj @@ -29,6 +29,7 @@ + diff --git a/NightGlow/Properties/Settings.Designer.cs b/NightGlow/Properties/Settings.Designer.cs index f603022..ddcac2f 100644 --- a/NightGlow/Properties/Settings.Designer.cs +++ b/NightGlow/Properties/Settings.Designer.cs @@ -190,5 +190,17 @@ public string HotKeyBrightTempDec { this["HotKeyBrightTempDec"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string DdcConfigJson { + get { + return ((string)(this["DdcConfigJson"])); + } + set { + this["DdcConfigJson"] = value; + } + } } } diff --git a/NightGlow/Properties/Settings.settings b/NightGlow/Properties/Settings.settings index e9e155a..52d1689 100644 --- a/NightGlow/Properties/Settings.settings +++ b/NightGlow/Properties/Settings.settings @@ -44,5 +44,8 @@ + + + \ No newline at end of file diff --git a/NightGlow/Services/DdcService.cs b/NightGlow/Services/DdcService.cs new file mode 100644 index 0000000..0f6432c --- /dev/null +++ b/NightGlow/Services/DdcService.cs @@ -0,0 +1,34 @@ +using NightGlow.MonitorConfig; + +namespace NightGlow.Services; + +public class DdcService +{ + + public Monitors Monitors; + + public DdcService() + { + + } + + public void UpdateMonitors() + { + if (Monitors == null) + { + Monitors = new Monitors(); + } + Monitors.Scan(); + } + + public void SetBrightness(VirtualMonitor vm, PhysicalMonitor pm, int value) + { + pm.SetBrightness((uint)value); + } + + public void SetContrast(VirtualMonitor vm, PhysicalMonitor pm, int value) + { + pm.SetContrast((uint)value); + } + +} diff --git a/NightGlow/Services/GammaService.cs b/NightGlow/Services/GammaService.cs index a5c13bb..ab72fc6 100644 --- a/NightGlow/Services/GammaService.cs +++ b/NightGlow/Services/GammaService.cs @@ -19,7 +19,7 @@ public partial class GammaService : IDisposable private bool _areDeviceContextsValid; private DateTimeOffset _lastGammaInvalidationTimestamp = DateTimeOffset.MinValue; - private ColorConfiguration? _lastConfiguration; + private IDictionary _lastConfigurations = new Dictionary(); private DateTimeOffset _lastUpdateTimestamp = DateTimeOffset.MinValue; public GammaService(SettingsService settingsService) @@ -104,7 +104,7 @@ private void EnsureValidDeviceContexts() _deviceContexts.DisposeAll(); _deviceContexts = DeviceContext.GetAllScreens(); - _lastConfiguration = null; + _lastConfigurations.Clear(); } private bool IsGammaStale() @@ -123,21 +123,22 @@ private bool IsGammaStale() return false; } - private bool IsSignificantChange(ColorConfiguration configuration) + private bool IsSignificantChange(ColorConfiguration configuration, string deviceName) { - // Nothing to compare to - if (_lastConfiguration is not { } lastConfiguration) - return true; + _lastConfigurations.TryGetValue(deviceName, out ColorConfiguration? lastConfigurationNullable); + if (lastConfigurationNullable == null) return true; + + ColorConfiguration lastConfiguration = (ColorConfiguration)lastConfigurationNullable; return Math.Abs(configuration.Temperature - lastConfiguration.Temperature) > 15 || Math.Abs(configuration.Brightness - lastConfiguration.Brightness) > 0.01; } - public void SetGamma(ColorConfiguration configuration) + public void SetDeviceGamma(ColorConfiguration configuration, string deviceName) { // Avoid unnecessary changes as updating too often will cause stutters - if (!IsGammaStale() && !IsSignificantChange(configuration)) + if (!IsGammaStale() && !IsSignificantChange(configuration, deviceName)) return; EnsureValidDeviceContexts(); @@ -146,20 +147,48 @@ public void SetGamma(ColorConfiguration configuration) foreach (var deviceContext in _deviceContexts) { + if (!deviceContext.DeviceName.Equals(deviceName)) continue; deviceContext.SetGamma( GetRed(configuration) * configuration.Brightness, GetGreen(configuration) * configuration.Brightness, GetBlue(configuration) * configuration.Brightness ); + break; } _isUpdatingGamma = false; - _lastConfiguration = configuration; + _lastConfigurations[deviceName] = configuration; _lastUpdateTimestamp = DateTimeOffset.Now; - Debug.WriteLine($"Updated gamma to {configuration}."); + //Debug.WriteLine($"Updated gamma to {configuration}."); } + //public void SetAllGamma(ColorConfiguration configuration) + //{ + // // Avoid unnecessary changes as updating too often will cause stutters + // if (!IsGammaStale() && !IsSignificantChange(configuration)) + // return; + + // EnsureValidDeviceContexts(); + + // _isUpdatingGamma = true; + + // foreach (var deviceContext in _deviceContexts) + // { + // deviceContext.SetGamma( + // GetRed(configuration) * configuration.Brightness, + // GetGreen(configuration) * configuration.Brightness, + // GetBlue(configuration) * configuration.Brightness + // ); + // } + + // _isUpdatingGamma = false; + + // _lastConfiguration = configuration; + // _lastUpdateTimestamp = DateTimeOffset.Now; + // Debug.WriteLine($"Updated gamma to {configuration}."); + //} + public void Dispose() { // Reset gamma on all contexts diff --git a/NightGlow/Services/NightGlowService.cs b/NightGlow/Services/NightGlowService.cs index 5fdc420..cb03974 100644 --- a/NightGlow/Services/NightGlowService.cs +++ b/NightGlow/Services/NightGlowService.cs @@ -1,7 +1,10 @@ using CommunityToolkit.Mvvm.ComponentModel; +using NightGlow.Data; using NightGlow.Models; +using NightGlow.MonitorConfig; using System; using System.Diagnostics; +using System.Linq; namespace NightGlow.Services; @@ -15,6 +18,8 @@ public class NightGlowService : ObservableObject, IDisposable private readonly GammaService _gammaService; + private readonly DdcService _ddcService; + public double Brightness { get => ColorConfig.Brightness; @@ -32,12 +37,14 @@ public int Temperature public NightGlowService( SettingsService settingsService, HotKeyService hotKeyService, - GammaService gammaService + GammaService gammaService, + DdcService ddcService ) { _settingsService = settingsService; _hotKeyService = hotKeyService; _gammaService = gammaService; + _ddcService = ddcService; RegisterHotKeys(); } @@ -130,9 +137,64 @@ private void ChangeConfig(double brightnessOffset, int temperatureOffset) UpdateConfiguration(); } + // Using the brightness value, check wheter to use ddc/gamma to set the brightness + // E.g. if ddcMonitorItem.MinBrightnessPct is 60 and DDC is enabled for the monitor, use the ranges below + // + // 0 -------------- 60 -------- 100 + // | Use gamma | Use DDC | + // + // DDC used first, then gamma is used to decrease brightness further private void UpdateConfiguration() { - _gammaService.SetGamma(ColorConfig); + Debug.WriteLine(""); + + foreach (VirtualMonitor vm in _ddcService.Monitors.VirtualMonitors) + { + PhysicalMonitor? pm = vm.PhysicalMonitors.FirstOrDefault(); + if (pm == null) continue; + + int configBrightnessPct = Convert.ToInt32(Brightness * 100); + + DdcMonitorItem ddcMonitorItem = _settingsService.DdcConfig.GetOrCreateDdcMonitorItem(vm); + + if (ddcMonitorItem.EnableDdc && configBrightnessPct > ddcMonitorItem.MinBrightnessPct) + { + // Use DDC to set the brightness + + // Find the percentage value the brightness is between lower (ddcMonitorItem.MinBrightnessPct) and upper (100) + double ddcRangePct = (double)(configBrightnessPct - ddcMonitorItem.MinBrightnessPct) / (100 - ddcMonitorItem.MinBrightnessPct); + int ddcBrightness = Convert.ToInt32(ddcRangePct * ddcMonitorItem.MaxBrightness); + + ColorConfiguration gamma = new ColorConfiguration(Temperature, _settingsService.BrightnessMax); + + Debug.WriteLine($"Using DDC. DDC: {ddcBrightness}. Gamma: {gamma.Brightness}. Monitor: {ddcMonitorItem.Name}"); + _ddcService.SetBrightness(vm, pm, ddcBrightness); + _gammaService.SetDeviceGamma(gamma, vm.DeviceName); + + } + else if (ddcMonitorItem.EnableDdc) + { + // Use Use gamma to set the brightness when DDC brightness is at minimum + + double gammaRangePct = (double)(configBrightnessPct - _settingsService.BrightnessMin) / (ddcMonitorItem.MinBrightnessPct - _settingsService.BrightnessMin); + double gammaBrightness = gammaRangePct; + + ColorConfiguration gamma = new ColorConfiguration(Temperature, gammaBrightness); + + Debug.WriteLine($"Using Gam. DDC: 0. Gamma: {gammaBrightness}. Monitor: {ddcMonitorItem.Name}"); + _ddcService.SetBrightness(vm, pm, 0); + _gammaService.SetDeviceGamma(gamma, vm.DeviceName); + + } + else + { + // Use Use gamma to set the brightness + + Debug.WriteLine($"Using Gam. DDC: x. Gamma: {Brightness}. Monitor: {ddcMonitorItem.Name}"); + _gammaService.SetDeviceGamma(ColorConfig, vm.DeviceName); + + } + } } public void Dispose() diff --git a/NightGlow/Services/SettingsService.cs b/NightGlow/Services/SettingsService.cs index 00f1448..9fc9e05 100644 --- a/NightGlow/Services/SettingsService.cs +++ b/NightGlow/Services/SettingsService.cs @@ -1,13 +1,18 @@ using CommunityToolkit.Mvvm.ComponentModel; +using Newtonsoft.Json; +using NightGlow.Data; using NightGlow.Models; using NightGlow.Properties; using System; +using System.Diagnostics; namespace NightGlow.Services; public class SettingsService : ObservableObject, IDisposable { + public DdcConfig DdcConfig; + private readonly RegSwitch _startOnBoot = new( @"HKCU\Software\Microsoft\Windows\CurrentVersion\Run", App.Name, @@ -172,7 +177,28 @@ public HotKey HotKeyBrightTempDec public SettingsService() { + LoadDdcConfig(); + } + + public void LoadDdcConfig() + { + string ddcConfigJson = Settings.Default.DdcConfigJson; + try + { + DdcConfig = JsonConvert.DeserializeObject(ddcConfigJson) ?? new DdcConfig(); + } + catch (JsonSerializationException ex) + { + Debug.WriteLine(string.Format("An error occurred during app config deserialization: {0}", ex.Message)); + Debug.WriteLine(ex); + } + } + public void SaveDdcConfig() + { + string ddcConfigJson = JsonConvert.SerializeObject(DdcConfig); + Settings.Default.DdcConfigJson = ddcConfigJson; + Settings.Default.Save(); } public void Dispose() diff --git a/NightGlow/ViewModels/MainWindowViewModel.cs b/NightGlow/ViewModels/MainWindowViewModel.cs index 07d5c47..9cc7426 100644 --- a/NightGlow/ViewModels/MainWindowViewModel.cs +++ b/NightGlow/ViewModels/MainWindowViewModel.cs @@ -1,6 +1,11 @@ using CommunityToolkit.Mvvm.ComponentModel; +using NightGlow.Data; +using NightGlow.MonitorConfig; using NightGlow.Services; +using System; +using System.Collections.ObjectModel; using System.ComponentModel; +using System.Linq; using System.Windows; namespace NightGlow.ViewModels; @@ -9,11 +14,17 @@ public class MainWindowViewModel : ObservableObject { private readonly SettingsService _settingsService; + private readonly DdcService _ddcService; private readonly NightGlowService _nightGlowService; public SettingsService SettingsService { get => _settingsService; } public NightGlowService NightGlowService { get => _nightGlowService; } + + public ObservableCollection MonitorItems { get; set; } + = new ObservableCollection(); + + private int _selectedTabIndex; public int SelectedTabIndex @@ -36,10 +47,73 @@ public int SelectedTabIndex } } - public MainWindowViewModel(SettingsService settingsService, NightGlowService nightGlowService) + public MainWindowViewModel( + SettingsService settingsService, + DdcService ddcService, + NightGlowService nightGlowService + ) { _settingsService = settingsService; + _ddcService = ddcService; _nightGlowService = nightGlowService; + + UpdateDdcMonitors(); + SetupMonitorItems(); + } + + public void SetupMonitorItems() + { + MonitorItems.Clear(); + + foreach (VirtualMonitor vm in _ddcService.Monitors.VirtualMonitors) + { + PhysicalMonitor? pm = vm.PhysicalMonitors.FirstOrDefault(); + if (pm == null) continue; + + DdcMonitorItem ddcMonitorItem = _settingsService.DdcConfig.GetOrCreateDdcMonitorItem(vm); + + MonitorItemViewModel monitorItemViewModel = new MonitorItemViewModel + { + VirtualName = vm.FriendlyName, + DeviceName = vm.DeviceName, + PhysicalName = pm.Description, + Brightness = Convert.ToInt32(pm.GetBrightness().Current), + Contrast = Convert.ToInt32(pm.GetContrast().Current), + EnableDdc = ddcMonitorItem.EnableDdc, + MaxBrightness = ddcMonitorItem.MaxBrightness, + MinBrightnessPct = ddcMonitorItem.MinBrightnessPct + }; + monitorItemViewModel.BrightnessChangeEvent += (object sender, DdcItemChangeEventArgs e) => + { + _ddcService.SetBrightness(vm, pm, e.MonitorItem.Brightness); + }; + monitorItemViewModel.ContrastChangeEvent += (object sender, DdcItemChangeEventArgs e) => + { + _ddcService.SetContrast(vm, pm, e.MonitorItem.Contrast); + }; + monitorItemViewModel.EnableDdcChangeEvent += (object sender, DdcItemChangeEventArgs e) => + { + _settingsService.DdcConfig.SetEnableDdc(vm, e.MonitorItem.EnableDdc); + _settingsService.SaveDdcConfig(); + }; + monitorItemViewModel.MinBrightnessPctChangeEvent += (object sender, DdcItemChangeEventArgs e) => + { + _settingsService.DdcConfig.SetMinBrightnessPct(vm, e.MonitorItem.MinBrightnessPct); + _settingsService.SaveDdcConfig(); + }; + monitorItemViewModel.MaxBrightnessChangeEvent += (object sender, DdcItemChangeEventArgs e) => + { + _settingsService.DdcConfig.SetMaxBrightness(vm, e.MonitorItem.MaxBrightness); + _settingsService.SaveDdcConfig(); + }; + MonitorItems.Add(monitorItemViewModel); + } + } + + public void UpdateDdcMonitors() + { + _ddcService.UpdateMonitors(); + SetupMonitorItems(); } public void OnWindowLoaded(object sender, RoutedEventArgs e) @@ -55,3 +129,84 @@ public void OnWindowClosing(object sender, CancelEventArgs e) } } + +public class MonitorItemViewModel : ObservableObject +{ + + public string VirtualName { get; set; } + public string DeviceName { get; set; } + public string PhysicalName { get; set; } + + private int _brightness; + private int _contrast; + private bool _enableDdc; + private int _minBrightnessPct; + private int _maxBrightness; + + public int Brightness + { + get => _brightness; + set + { + _brightness = value; + OnPropertyChanged(nameof(Brightness)); + BrightnessChangeEvent?.Invoke(null, new DdcItemChangeEventArgs { MonitorItem = this }); + } + } + + public int Contrast + { + get => _contrast; + set + { + _contrast = value; + OnPropertyChanged(nameof(Contrast)); + ContrastChangeEvent?.Invoke(null, new DdcItemChangeEventArgs { MonitorItem = this }); + } + } + + public bool EnableDdc + { + get => _enableDdc; + set + { + _enableDdc = value; + OnPropertyChanged(nameof(EnableDdc)); + EnableDdcChangeEvent?.Invoke(null, new DdcItemChangeEventArgs { MonitorItem = this }); + } + } + + public int MinBrightnessPct + { + get => _minBrightnessPct; + set + { + _minBrightnessPct = value; + OnPropertyChanged(nameof(MinBrightnessPct)); + MinBrightnessPctChangeEvent?.Invoke(null, new DdcItemChangeEventArgs { MonitorItem = this }); + } + } + + public int MaxBrightness + { + get => _maxBrightness; + set + { + _maxBrightness = value; + OnPropertyChanged(nameof(MaxBrightness)); + MaxBrightnessChangeEvent?.Invoke(null, new DdcItemChangeEventArgs { MonitorItem = this }); + } + } + + public event EventHandler EnableDdcChangeEvent; + public event EventHandler BrightnessChangeEvent; + public event EventHandler ContrastChangeEvent; + public event EventHandler MinBrightnessPctChangeEvent; + public event EventHandler MaxBrightnessChangeEvent; + +} + +public class DdcItemChangeEventArgs : EventArgs +{ + public MonitorItemViewModel MonitorItem { get; set; } +} diff --git a/NightGlow/Views/MainWindow.xaml b/NightGlow/Views/MainWindow.xaml index ac59c72..ebfc6ba 100644 --- a/NightGlow/Views/MainWindow.xaml +++ b/NightGlow/Views/MainWindow.xaml @@ -116,6 +116,28 @@ + + + + + + + + + + + + diff --git a/NightGlow/Views/SettingsDdcView.xaml b/NightGlow/Views/SettingsDdcView.xaml new file mode 100644 index 0000000..4970356 --- /dev/null +++ b/NightGlow/Views/SettingsDdcView.xaml @@ -0,0 +1,170 @@ + + + + + + + + + + + +