From f7500e929c8c4bb2c08403b4567d2c6aa11d244f Mon Sep 17 00:00:00 2001 From: emoacht Date: Thu, 23 Feb 2023 19:03:23 +0900 Subject: [PATCH 01/13] Add GetDpiForSystem function to get system DPI --- Source/ScreenFrame/VisualTreeHelperAddition.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/ScreenFrame/VisualTreeHelperAddition.cs b/Source/ScreenFrame/VisualTreeHelperAddition.cs index b7f33be8..87244b00 100644 --- a/Source/ScreenFrame/VisualTreeHelperAddition.cs +++ b/Source/ScreenFrame/VisualTreeHelperAddition.cs @@ -37,6 +37,9 @@ private static extern int GetDeviceCaps( private const int LOGPIXELSX = 88; private const int LOGPIXELSY = 90; + [DllImport("User32.dll")] + private static extern uint GetDpiForSystem(); + [DllImport("Shcore.dll")] private static extern int GetDpiForMonitor( IntPtr hmonitor, @@ -80,6 +83,14 @@ private enum MONITOR_DPI_TYPE private static DpiScale GetSystemDpi() { + if (OsVersion.Is10Build14393OrGreater) + { + double pixelsPerInch = GetDpiForSystem(); + return new DpiScale( + pixelsPerInch / DefaultPixelsPerInch, + pixelsPerInch / DefaultPixelsPerInch); + } + var handle = IntPtr.Zero; try { From 0f2660ddfa5a102652a0877751851a788957d13d Mon Sep 17 00:00:00 2001 From: emoacht Date: Sat, 25 Feb 2023 06:26:44 +0900 Subject: [PATCH 02/13] Refactor --- Source/Monitorian.Core/Helper/OsVersion.cs | 2 +- .../Models/Monitor/DdcMonitorItem.cs | 4 ++-- .../Models/Monitor/DeviceInformation.cs | 2 +- .../Models/Monitor/LightSensor.cs | 6 +++--- .../Models/Monitor/MSMonitor.cs | 2 +- .../Models/Monitor/MonitorManager.cs | 4 ++-- .../Models/Monitor/PowerManagement.cs | 16 ++++++++-------- .../Models/Monitor/WmiMonitorItem.cs | 2 +- .../Views/Controls/Sliders/RangeSlider.cs | 14 ++++++++++---- .../Views/Controls/Sliders/ShadowSlider.cs | 19 +++++++++++++------ Source/ScreenFrame/Helper/OsVersion.cs | 2 +- Source/StartupAgency/Helper/OsVersion.cs | 2 +- 12 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Source/Monitorian.Core/Helper/OsVersion.cs b/Source/Monitorian.Core/Helper/OsVersion.cs index 7503166c..7cf9076a 100644 --- a/Source/Monitorian.Core/Helper/OsVersion.cs +++ b/Source/Monitorian.Core/Helper/OsVersion.cs @@ -52,7 +52,7 @@ internal static class OsVersion private static readonly Dictionary _cache = new(); private static readonly object _lock = new(); - private static bool IsEqualToOrGreaterThan(int major, int minor = 0, int build = 0, [CallerMemberName] string propertyName = null) + private static bool IsEqualToOrGreaterThan(in int major, in int minor = 0, in int build = 0, [CallerMemberName] string propertyName = null) { lock (_lock) { diff --git a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs index eec55b6b..da0f5ba1 100644 --- a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs @@ -63,7 +63,7 @@ public override AccessResult UpdateBrightness(int brightness = -1) public override AccessResult SetBrightness(int brightness) { if (brightness is < 0 or > 100) - throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be within 0 to 100."); + throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be from 0 to 100."); var buffer = (uint)Math.Round(brightness / 100D * (_maximumBrightness - _minimumBrightness) + _minimumBrightness, MidpointRounding.AwayFromZero); @@ -99,7 +99,7 @@ public override AccessResult UpdateContrast() public override AccessResult SetContrast(int contrast) { if (contrast is < 0 or > 100) - throw new ArgumentOutOfRangeException(nameof(contrast), contrast, "The contrast must be within 0 to 100."); + throw new ArgumentOutOfRangeException(nameof(contrast), contrast, "The contrast must be from 0 to 100."); var buffer = (uint)Math.Round(contrast / 100D * (_maximumContrast - _minimumContrast) + _minimumContrast, MidpointRounding.AwayFromZero); diff --git a/Source/Monitorian.Core/Models/Monitor/DeviceInformation.cs b/Source/Monitorian.Core/Models/Monitor/DeviceInformation.cs index 59e6f5e2..a62fd167 100644 --- a/Source/Monitorian.Core/Models/Monitor/DeviceInformation.cs +++ b/Source/Monitorian.Core/Models/Monitor/DeviceInformation.cs @@ -188,7 +188,7 @@ public InstalledItem( #endregion - private static readonly Guid GUID_DEVINTERFACE_MONITOR = new Guid("E6F07B5F-EE97-4a90-B076-33F57BF4EAA7"); + private static readonly Guid GUID_DEVINTERFACE_MONITOR = new("E6F07B5F-EE97-4a90-B076-33F57BF4EAA7"); public static IEnumerable EnumerateInstalledMonitors() { diff --git a/Source/Monitorian.Core/Models/Monitor/LightSensor.cs b/Source/Monitorian.Core/Models/Monitor/LightSensor.cs index 7159c819..5521b5e1 100644 --- a/Source/Monitorian.Core/Models/Monitor/LightSensor.cs +++ b/Source/Monitorian.Core/Models/Monitor/LightSensor.cs @@ -79,12 +79,12 @@ private interface ISensor uint GetFriendlyName(out string friendlyName); } - private static uint S_OK = 0x0; - private static uint E_ELEMENTNOTFOUND = 0x80070490; // 0x80070490 means 0x0490 -> 1168 -> ERROR_NOT_FOUND + private const uint S_OK = 0x0; + private const uint E_ELEMENTNOTFOUND = 0x80070490; // 0x80070490 means 0x0490 -> 1168 -> ERROR_NOT_FOUND #endregion - private static Guid SENSOR_TYPE_AMBIENT_LIGHT => new Guid("97F115C8-599A-4153-8894-D2D12899918A"); + private static Guid SENSOR_TYPE_AMBIENT_LIGHT => new("97F115C8-599A-4153-8894-D2D12899918A"); public static bool AmbientLightSensorExists => _ambientLightSensorExists.Value; private static readonly Lazy _ambientLightSensorExists = new(() => SensorExists(SENSOR_TYPE_AMBIENT_LIGHT)); diff --git a/Source/Monitorian.Core/Models/Monitor/MSMonitor.cs b/Source/Monitorian.Core/Models/Monitor/MSMonitor.cs index 12d42776..02d957ee 100644 --- a/Source/Monitorian.Core/Models/Monitor/MSMonitor.cs +++ b/Source/Monitorian.Core/Models/Monitor/MSMonitor.cs @@ -194,7 +194,7 @@ public static bool SetBrightness(string deviceInstanceId, int brightness, int ti if (string.IsNullOrWhiteSpace(deviceInstanceId)) throw new ArgumentNullException(nameof(deviceInstanceId)); if (brightness is < 0 or > 100) - throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be within 0 to 100."); + throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be from 0 to 100."); try { diff --git a/Source/Monitorian.Core/Models/Monitor/MonitorManager.cs b/Source/Monitorian.Core/Models/Monitor/MonitorManager.cs index dd3709c4..08557f14 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorManager.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorManager.cs @@ -248,10 +248,10 @@ public static async Task ProbeMonitorsAsync() [DataContract] private class PhysicalItemPlus : MonitorConfiguration.PhysicalItem { - [DataMember(Order = 6)] + [DataMember(Order = 3)] public string GetBrightness { get; private set; } - [DataMember(Order = 7)] + [DataMember(Order = 4)] public string SetBrightness { get; private set; } public PhysicalItemPlus( diff --git a/Source/Monitorian.Core/Models/Monitor/PowerManagement.cs b/Source/Monitorian.Core/Models/Monitor/PowerManagement.cs index ba50eac7..4a6a71ad 100644 --- a/Source/Monitorian.Core/Models/Monitor/PowerManagement.cs +++ b/Source/Monitorian.Core/Models/Monitor/PowerManagement.cs @@ -81,14 +81,14 @@ private struct SYSTEM_POWER_STATUS #endregion // Video settings derived from winnt.h - private static readonly Guid VIDEO_SUBGROUP = new Guid("7516b95f-f776-4464-8c53-06167f40cc99"); - private static readonly Guid VIDEO_ADAPTIVE_DISPLAY_BRIGHTNESS = new Guid("fbd9aa66-9553-4097-ba44-ed6e9d65eab8"); - private static readonly Guid DEVICE_POWER_POLICY_VIDEO_BRIGHTNESS = new Guid("aded5e82-b909-4619-9949-f5d71dac0bcb"); - private static readonly Guid DEVICE_POWER_POLICY_VIDEO_DIM_BRIGHTNESS = new Guid("f1fbfde2-a960-4165-9f88-50667911ce96"); - private static readonly Guid CONSOLE_DISPLAY_STATE = new Guid("6fe69556-704a-47a0-8f24-c28d936fda47"); + private static readonly Guid VIDEO_SUBGROUP = new("7516b95f-f776-4464-8c53-06167f40cc99"); + private static readonly Guid VIDEO_ADAPTIVE_DISPLAY_BRIGHTNESS = new("fbd9aa66-9553-4097-ba44-ed6e9d65eab8"); + private static readonly Guid DEVICE_POWER_POLICY_VIDEO_BRIGHTNESS = new("aded5e82-b909-4619-9949-f5d71dac0bcb"); + private static readonly Guid DEVICE_POWER_POLICY_VIDEO_DIM_BRIGHTNESS = new("f1fbfde2-a960-4165-9f88-50667911ce96"); + private static readonly Guid CONSOLE_DISPLAY_STATE = new("6fe69556-704a-47a0-8f24-c28d936fda47"); // AC/DC power source derived from winnt.h - private static readonly Guid ACDC_POWER_SOURCE = new Guid("5d3e9a59-e9d5-4b00-a6bd-ff34ff516548"); + private static readonly Guid ACDC_POWER_SOURCE = new("5d3e9a59-e9d5-4b00-a6bd-ff34ff516548"); public static Guid GetActiveScheme() { @@ -322,7 +322,7 @@ public static int GetActiveSchemeBrightness() public static bool SetActiveSchemeBrightness(int brightness) { if (brightness is < 0 or > 100) - throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be within 0 to 100."); + throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be from 0 to 100."); var isOnline = IsOnline(); if (!isOnline.HasValue) @@ -352,7 +352,7 @@ public static bool SetActiveSchemeBrightness(int brightness) DEVICE_POWER_POLICY_VIDEO_BRIGHTNESS, (uint)brightness) != ERROR_SUCCESS) { - Debug.WriteLine("Failed to write DC Brightness"); + Debug.WriteLine("Failed to write DC Brightness."); return false; } } diff --git a/Source/Monitorian.Core/Models/Monitor/WmiMonitorItem.cs b/Source/Monitorian.Core/Models/Monitor/WmiMonitorItem.cs index cd20794a..3115416a 100644 --- a/Source/Monitorian.Core/Models/Monitor/WmiMonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/WmiMonitorItem.cs @@ -59,7 +59,7 @@ public override AccessResult UpdateBrightness(int brightness = -1) public override AccessResult SetBrightness(int brightness) { if (brightness is < 0 or > 100) - throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be within 0 to 100."); + throw new ArgumentOutOfRangeException(nameof(brightness), brightness, "The brightness must be from 0 to 100."); if (IsInternal) { diff --git a/Source/Monitorian.Core/Views/Controls/Sliders/RangeSlider.cs b/Source/Monitorian.Core/Views/Controls/Sliders/RangeSlider.cs index 1199ea14..ff1f51a1 100644 --- a/Source/Monitorian.Core/Views/Controls/Sliders/RangeSlider.cs +++ b/Source/Monitorian.Core/Views/Controls/Sliders/RangeSlider.cs @@ -11,8 +11,8 @@ namespace Monitorian.Core.Views.Controls { - [TemplatePart(Name = "PART_StartTrack", Type = typeof(Track))] - [TemplatePart(Name = "PART_EndTrack", Type = typeof(Track))] + [TemplatePart(Name = nameof(Named.PART_StartTrack), Type = typeof(Track))] + [TemplatePart(Name = nameof(Named.PART_EndTrack), Type = typeof(Track))] public class RangeSlider : EnhancedSlider { static RangeSlider() @@ -108,6 +108,12 @@ private void ReflectSelectionRange() || (this.Minimum < this.SelectionStart) || (this.SelectionEnd < this.Maximum); } + private enum Named + { + PART_StartTrack, + PART_EndTrack + } + private Track _startTrack; private Track _endTrack; @@ -116,8 +122,8 @@ private void FindTemplateMembers() ClearBindingAndRemoveDragEventHandler(_startTrack); ClearBindingAndRemoveDragEventHandler(_endTrack); - _startTrack = this.GetTemplateChild("PART_StartTrack") as Track; - _endTrack = this.GetTemplateChild("PART_EndTrack") as Track; + _startTrack = this.GetTemplateChild(nameof(Named.PART_StartTrack)) as Track; + _endTrack = this.GetTemplateChild(nameof(Named.PART_EndTrack)) as Track; SetBindingAndAddDragEventHandler(_startTrack, nameof(Slider.SelectionStart)); SetBindingAndAddDragEventHandler(_endTrack, nameof(Slider.SelectionEnd)); diff --git a/Source/Monitorian.Core/Views/Controls/Sliders/ShadowSlider.cs b/Source/Monitorian.Core/Views/Controls/Sliders/ShadowSlider.cs index c221bcac..30a8abf5 100644 --- a/Source/Monitorian.Core/Views/Controls/Sliders/ShadowSlider.cs +++ b/Source/Monitorian.Core/Views/Controls/Sliders/ShadowSlider.cs @@ -8,9 +8,9 @@ namespace Monitorian.Core.Views.Controls { - [TemplatePart(Name = "PART_ShadowThumb", Type = typeof(FrameworkElement))] - [TemplatePart(Name = "PART_ShadowLeft", Type = typeof(ColumnDefinition))] - [TemplatePart(Name = "PART_ShadowRight", Type = typeof(ColumnDefinition))] + [TemplatePart(Name = nameof(Named.PART_ShadowThumb), Type = typeof(FrameworkElement))] + [TemplatePart(Name = nameof(Named.PART_ShadowLeft), Type = typeof(ColumnDefinition))] + [TemplatePart(Name = nameof(Named.PART_ShadowRight), Type = typeof(ColumnDefinition))] public class ShadowSlider : RangeSlider { public override void OnApplyTemplate() @@ -58,15 +58,22 @@ private void CheckCanUseShadow() CanUseShadow = FindTemplateMembers(); } + private enum Named + { + PART_ShadowThumb, + PART_ShadowLeft, + PART_ShadowRight + } + private FrameworkElement _shadowThumb; private ColumnDefinition _shadowLeft; private ColumnDefinition _shadowRight; private bool FindTemplateMembers() { - _shadowThumb = this.GetTemplateChild("PART_ShadowThumb") as FrameworkElement; - _shadowLeft = this.GetTemplateChild("PART_ShadowLeft") as ColumnDefinition; - _shadowRight = this.GetTemplateChild("PART_ShadowRight") as ColumnDefinition; + _shadowThumb = this.GetTemplateChild(nameof(Named.PART_ShadowThumb)) as FrameworkElement; + _shadowLeft = this.GetTemplateChild(nameof(Named.PART_ShadowLeft)) as ColumnDefinition; + _shadowRight = this.GetTemplateChild(nameof(Named.PART_ShadowRight)) as ColumnDefinition; return (_shadowThumb is not null) && (_shadowLeft is not null) diff --git a/Source/ScreenFrame/Helper/OsVersion.cs b/Source/ScreenFrame/Helper/OsVersion.cs index 68cb3b63..92ea55f3 100644 --- a/Source/ScreenFrame/Helper/OsVersion.cs +++ b/Source/ScreenFrame/Helper/OsVersion.cs @@ -53,7 +53,7 @@ internal static class OsVersion private static readonly Dictionary _cache = new(); private static readonly object _lock = new(); - private static bool IsEqualToOrGreaterThan(int major, int minor = 0, int build = 0, [CallerMemberName] string propertyName = null) + private static bool IsEqualToOrGreaterThan(in int major, in int minor = 0, in int build = 0, [CallerMemberName] string propertyName = null) { lock (_lock) { diff --git a/Source/StartupAgency/Helper/OsVersion.cs b/Source/StartupAgency/Helper/OsVersion.cs index 23c9ece1..31b27438 100644 --- a/Source/StartupAgency/Helper/OsVersion.cs +++ b/Source/StartupAgency/Helper/OsVersion.cs @@ -48,7 +48,7 @@ internal static class OsVersion private static readonly Dictionary _cache = new(); private static readonly object _lock = new(); - private static bool IsEqualToOrGreaterThan(int major, int minor = 0, int build = 0, [CallerMemberName] string propertyName = null) + private static bool IsEqualToOrGreaterThan(in int major, in int minor = 0, in int build = 0, [CallerMemberName] string propertyName = null) { lock (_lock) { From 070d30441a8872ff59973b974885d272a95900dc Mon Sep 17 00:00:00 2001 From: emoacht Date: Tue, 7 Mar 2023 23:56:04 +0900 Subject: [PATCH 03/13] Add native resolution and change physical size --- .../Models/Monitor/DisplayMonitor.cs | 8 ++-- .../DisplayInformation.cs | 48 ++++++++++++------- .../Monitorian.Supplement.csproj | 1 + 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/Source/Monitorian.Core/Models/Monitor/DisplayMonitor.cs b/Source/Monitorian.Core/Models/Monitor/DisplayMonitor.cs index 13e224ed..d5c992f9 100644 --- a/Source/Monitorian.Core/Models/Monitor/DisplayMonitor.cs +++ b/Source/Monitorian.Core/Models/Monitor/DisplayMonitor.cs @@ -29,21 +29,21 @@ public class DisplayItem : IDisplayItem public string DisplayName { get; } [DataMember(Order = 2)] - public bool IsInternal { get; } + public float PhysicalDiagonalLength { get; } [DataMember(Order = 3)] - public string ConnectionDescription { get; } + public bool IsInternal { get; } [DataMember(Order = 4)] - public float PhysicalSize { get; } + public string ConnectionDescription { get; } public DisplayItem(Monitorian.Supplement.DisplayInformation.DisplayItem item) { this.DeviceInstanceId = item.DeviceInstanceId; this.DisplayName = item.DisplayName; + this.PhysicalDiagonalLength = item.PhysicalDiagonalLength; this.IsInternal = item.IsInternal; this.ConnectionDescription = item.ConnectionDescription; - this.PhysicalSize = item.PhysicalSize; } } diff --git a/Source/Monitorian.Supplement/DisplayInformation.cs b/Source/Monitorian.Supplement/DisplayInformation.cs index cfaf3c30..49d67d45 100644 --- a/Source/Monitorian.Supplement/DisplayInformation.cs +++ b/Source/Monitorian.Supplement/DisplayInformation.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; using Windows.Devices.Display; using Windows.Devices.Enumeration; -using Windows.Foundation; namespace Monitorian.Supplement { @@ -23,7 +23,7 @@ public class DisplayInformation /// /// Display monitor information /// - public class DisplayItem + public record DisplayItem { /// /// Device ID (Not device interface ID) @@ -35,6 +35,21 @@ public class DisplayItem /// public string DisplayName { get; } + /// + /// Native resolution in raw pixels. + /// + public Size NativeResolution { get; } + + /// + /// Physical size in inches + /// + public Size PhysicalSize { get; } + + /// + /// Physical diagonal Length in inches + /// + public float PhysicalDiagonalLength => GetDiagonal(PhysicalSize); + /// /// Whether the display is connected internally /// @@ -45,24 +60,24 @@ public class DisplayItem /// public string ConnectionDescription { get; } - /// - /// Physical size (diagonal) in inches - /// - public float PhysicalSize { get; } - internal DisplayItem( string deviceInstanceId, string displayName, + Windows.Graphics.SizeInt32 nativeResolution, + Windows.Foundation.Size physicalSize, bool isInternal, - string connectionDescription = null, - float physicalSize = 0F) + string connectionDescription) { this.DeviceInstanceId = deviceInstanceId; this.DisplayName = displayName; + this.NativeResolution = new Size(nativeResolution.Width, nativeResolution.Height); + this.PhysicalSize = new Size(physicalSize.Width, physicalSize.Height); this.IsInternal = isInternal; this.ConnectionDescription = connectionDescription; - this.PhysicalSize = physicalSize; } + + private static float GetDiagonal(Size source) => + (float)Math.Sqrt(Math.Pow(source.Width, 2) + Math.Pow(source.Height, 2)); } #endregion @@ -109,16 +124,17 @@ public static async Task GetDisplayMonitorsAsync() //Debug.WriteLine($"DeviceInstanceId: {deviceInstanceId}"); //Debug.WriteLine($"DisplayName: {displayMonitor.DisplayName}"); + //Debug.WriteLine($"NativeResolution: {displayMonitor.NativeResolutionInRawPixels.Width},{displayMonitor.NativeResolutionInRawPixels.Height}"); + //Debug.WriteLine($"PhysicalSize: {displayMonitor.PhysicalSizeInInches.Value.Width:F2},{displayMonitor.PhysicalSizeInInches.Value.Height:F2}"); //Debug.WriteLine($"ConnectionKind: {displayMonitor.ConnectionKind}"); - //Debug.WriteLine($"PhysicalConnector: {displayMonitor.PhysicalConnector}"); - //Debug.WriteLine($"PhysicalSize: {GetDiagonal(displayMonitor.PhysicalSizeInInches):F1}"); items.Add(new DisplayItem( deviceInstanceId: deviceInstanceId, displayName: displayMonitor.DisplayName, + nativeResolution: displayMonitor.NativeResolutionInRawPixels, + physicalSize: displayMonitor.PhysicalSizeInInches ?? default, isInternal: (displayMonitor.ConnectionKind == DisplayMonitorConnectionKind.Internal), - connectionDescription: GetConnectionDescription(displayMonitor.ConnectionKind, displayMonitor.PhysicalConnector), - physicalSize: GetDiagonal(displayMonitor.PhysicalSizeInInches))); + connectionDescription: GetConnectionDescription(displayMonitor.ConnectionKind, displayMonitor.PhysicalConnector))); } return items.ToArray(); } @@ -161,9 +177,5 @@ private static string GetConnectionDescription(DisplayMonitorConnectionKind conn } return null; } - - private static float GetDiagonal(Size? source) => source.HasValue - ? (float)Math.Sqrt(Math.Pow(source.Value.Width, 2) + Math.Pow(source.Value.Height, 2)) - : 0F; } } \ No newline at end of file diff --git a/Source/Monitorian.Supplement/Monitorian.Supplement.csproj b/Source/Monitorian.Supplement/Monitorian.Supplement.csproj index d54726fe..e640fd64 100644 --- a/Source/Monitorian.Supplement/Monitorian.Supplement.csproj +++ b/Source/Monitorian.Supplement/Monitorian.Supplement.csproj @@ -52,6 +52,7 @@ $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\$(TargetPlatformVersion)\Windows.winmd False + From 194bc6d7410b862d5d22a46d47a9e58caacb9fd1 Mon Sep 17 00:00:00 2001 From: Oleh Kaplun Date: Mon, 2 Jan 2023 17:18:32 +0200 Subject: [PATCH 04/13] Update Resources.uk-UA.resx --- .../Properties/Resources.uk-UA.resx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Monitorian.Core/Properties/Resources.uk-UA.resx b/Source/Monitorian.Core/Properties/Resources.uk-UA.resx index d97b0048..fcdbbfdf 100644 --- a/Source/Monitorian.Core/Properties/Resources.uk-UA.resx +++ b/Source/Monitorian.Core/Properties/Resources.uk-UA.resx @@ -53,6 +53,7 @@ value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. + mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter @@ -116,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Параметри командного рядка + Змінити контрастність @@ -126,7 +130,7 @@ Регулювати одночасно - Закрити + Закрити программу Копіювати журнал дій @@ -134,6 +138,9 @@ Зберегти журнал дій на робочий стіл? + + Зачекайте, поки додаткова інформація буде записана в журнал операцій. + Не змінювати яскравість до зупинки @@ -164,6 +171,9 @@ Знайти монітори + + Відновити яскравість після повторного підключення + Налаштування @@ -185,6 +195,9 @@ DDC/CI не увімкнено або не підтримується. + + Використовувати кольорову тему системи + Використовувати великі повзунки From de79e71bedd6c883af1cc9d2eb52c854bdca3f62 Mon Sep 17 00:00:00 2001 From: emoacht Date: Wed, 8 Mar 2023 02:00:20 +0900 Subject: [PATCH 05/13] Revert close --- Source/Monitorian.Core/Properties/Resources.uk-UA.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Monitorian.Core/Properties/Resources.uk-UA.resx b/Source/Monitorian.Core/Properties/Resources.uk-UA.resx index fcdbbfdf..da121f5b 100644 --- a/Source/Monitorian.Core/Properties/Resources.uk-UA.resx +++ b/Source/Monitorian.Core/Properties/Resources.uk-UA.resx @@ -130,7 +130,7 @@ Регулювати одночасно - Закрити программу + Закрити Копіювати журнал дій From c97b5e013fc8ad59bd83b4384d34ffdcfafce72c Mon Sep 17 00:00:00 2001 From: Alexander Ziborov <1420883+San4es@users.noreply.github.com> Date: Sun, 12 Feb 2023 23:56:08 +0300 Subject: [PATCH 06/13] Add ru translation for UseAccentColor --- Source/Monitorian.Core/Properties/Resources.ru-RU.resx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx index 5ab6e684..e9e7d21e 100644 --- a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx +++ b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx @@ -189,4 +189,7 @@ Использовать большие ползунки - \ No newline at end of file + + Использовать системный цвет для ползунка яркости + + From acc82fe62109f33b1c24c7f0671eb56548030a09 Mon Sep 17 00:00:00 2001 From: Alexander Ziborov <1420883+San4es@users.noreply.github.com> Date: Sun, 12 Feb 2023 23:58:58 +0300 Subject: [PATCH 07/13] Remove unneeded changes --- Source/Monitorian.Core/Properties/Resources.ru-RU.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx index e9e7d21e..9a3e0543 100644 --- a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx +++ b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx @@ -192,4 +192,4 @@ Использовать системный цвет для ползунка яркости - + \ No newline at end of file From 2d5ec7a1a0d118fb0ebfd92859bf870178f497d7 Mon Sep 17 00:00:00 2001 From: emoacht Date: Wed, 8 Mar 2023 02:23:57 +0900 Subject: [PATCH 08/13] Modify order --- Source/Monitorian.Core/Properties/Resources.ru-RU.resx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx index 9a3e0543..22696f58 100644 --- a/Source/Monitorian.Core/Properties/Resources.ru-RU.resx +++ b/Source/Monitorian.Core/Properties/Resources.ru-RU.resx @@ -186,10 +186,10 @@ DDC/CI не включен или не поддерживается. - - Использовать большие ползунки - Использовать системный цвет для ползунка яркости + + Использовать большие ползунки + \ No newline at end of file From ee570b59394a37fe8bd57850bd6a0cb85d97704d Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 12 Mar 2023 05:31:08 +0900 Subject: [PATCH 09/13] Add color temperature to probe monitors --- .../Models/Monitor/MonitorConfiguration.cs | 117 +++++++++++++++--- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs index e11e904a..eb8bb6ba 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs @@ -186,6 +186,7 @@ private enum VcpCode : byte None = 0x0, Luminance = 0x10, Contrast = 0x12, + Temperature = 0x14, SpeakerVolume = 0x62, PowerMode = 0xD6, } @@ -261,14 +262,14 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle capabilitiesStringLength)) { var capabilitiesString = buffer.ToString(); - var vcpCodes = EnumerateVcpCodes(capabilitiesString).ToArray(); + IReadOnlyCollection vcpCodes = EnumerateVcpCodes(capabilitiesString).ToArray(); return new MonitorCapability( isHighLevelBrightnessSupported: isHighLevelSupported, isLowLevelBrightnessSupported: vcpCodes.Contains((byte)VcpCode.Luminance), isContrastSupported: vcpCodes.Contains((byte)VcpCode.Contrast), capabilitiesString: (verbose ? capabilitiesString : null), - capabilitiesReport: (verbose ? MakeCapabilitiesReport(vcpCodes) : null), + capabilitiesReport: (verbose ? MakeCapabilitiesReport(capabilitiesString) : null), capabilitiesData: (verbose && !vcpCodes.Any() ? GetCapabilitiesData(physicalMonitorHandle, capabilitiesStringLength) : null)); } } @@ -277,12 +278,19 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle isLowLevelBrightnessSupported: false, isContrastSupported: false); - static string MakeCapabilitiesReport(byte[] vcpCodes) + static string MakeCapabilitiesReport(string capabilitiesString) { - return $"Luminance: {vcpCodes.Contains((byte)VcpCode.Luminance)}, " + - $"Contrast: {vcpCodes.Contains((byte)VcpCode.Contrast)}, " + - $"Speaker Volume: {vcpCodes.Contains((byte)VcpCode.SpeakerVolume)}, " + - $"Power Mode: {vcpCodes.Contains((byte)VcpCode.PowerMode)}"; + IReadOnlyDictionary vcpCodeValues = GetVcpCodeValues(capabilitiesString); + + var temperatureString = vcpCodeValues.TryGetValue((byte)VcpCode.Temperature, out byte[] values) + ? $"{true} ({values?.Intersect(new byte[] { 3, 4, 5, 6, 7, 8, 9, 10 }).Count() ?? 0})" + : false.ToString(); + + return $"Luminance: {vcpCodeValues.ContainsKey((byte)VcpCode.Luminance)}, " + + $"Contrast: {vcpCodeValues.ContainsKey((byte)VcpCode.Contrast)}, " + + $"Color Temperature: {temperatureString}, " + + $"Speaker Volume: {vcpCodeValues.ContainsKey((byte)VcpCode.SpeakerVolume)}, " + + $"Power Mode: {vcpCodeValues.ContainsKey((byte)VcpCode.PowerMode)}"; } static byte[] GetCapabilitiesData(SafePhysicalMonitorHandle physicalMonitorHandle, uint capabilitiesStringLength) @@ -315,7 +323,7 @@ private static IEnumerable EnumerateVcpCodes(string source) if (string.IsNullOrEmpty(source)) yield break; - int index = source.IndexOf("vcp", StringComparison.OrdinalIgnoreCase); + int index = source.IndexOf("vcp", StringComparison.Ordinal); if (index < 0) yield break; @@ -336,23 +344,18 @@ private static IEnumerable EnumerateVcpCodes(string source) depth--; if (depth < 1) { - if (0 < buffer.Length) - { - yield return byte.Parse(buffer.ToString(), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo); - } yield break; // End of enumeration } break; default: - if (depth == 1) + if (depth is 1) { if (IsHexNumber(c)) { buffer.Append(c); - if (buffer.Length == 1) + if (buffer.Length is 1) continue; } - if (0 < buffer.Length) { yield return byte.Parse(buffer.ToString(), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo); @@ -367,6 +370,90 @@ private static IEnumerable EnumerateVcpCodes(string source) static bool IsHexNumber(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f'); } + private static Dictionary GetVcpCodeValues(string source) + { + var dic = new Dictionary(); + + if (string.IsNullOrEmpty(source)) + return dic; + + int index = source.IndexOf("vcp", StringComparison.Ordinal); + if (index < 0) + return dic; + + int depth = 0; + var buffer1 = new StringBuilder(2); + byte? lastKey = null; + var buffer2 = new StringBuilder(2); + var values = new List(); + + foreach (char c in source.Skip(index + 3)) + { + if (!IsAscii(c)) + break; + + switch (c) + { + case '(': + depth++; + break; + case ')': + depth--; + switch (depth) + { + case < 1: + goto end; // End of enumeration + case 1: + if (values.Any() && lastKey.HasValue) + { + dic[lastKey.Value] = values.ToArray(); + values.Clear(); + } + break; + } + break; + default: + switch (depth) + { + case 1: + if (IsHexNumber(c)) + { + buffer1.Append(c); + if (buffer1.Length is 1) + continue; + } + if (0 < buffer1.Length) + { + lastKey = byte.Parse(buffer1.ToString(), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo); + buffer1.Clear(); + dic[lastKey.Value] = null; + } + break; + case 2: + if (IsHexNumber(c)) + { + buffer2.Append(c); + if (buffer2.Length is 1) + continue; + } + if (0 < buffer2.Length) + { + var value = byte.Parse(buffer2.ToString(), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo); + buffer2.Clear(); + values.Add(value); + } + break; + } + break; + } + } + end: + return dic; + + static bool IsAscii(char c) => c <= 0x7F; + static bool IsHexNumber(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f'); + } + /// /// Gets raw brightnesses not represented in percentage. /// From 31556cf6d1c64c245c5fc832b868e44d415e85ce Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 12 Mar 2023 05:36:45 +0900 Subject: [PATCH 10/13] Add color temperature to monitor capabilities test --- .../MonitorConfigurationTest.cs | 150 +++++++++++++++--- 1 file changed, 127 insertions(+), 23 deletions(-) diff --git a/Source/Monitorian.Test/MonitorConfigurationTest.cs b/Source/Monitorian.Test/MonitorConfigurationTest.cs index e4e6791a..8168dade 100644 --- a/Source/Monitorian.Test/MonitorConfigurationTest.cs +++ b/Source/Monitorian.Test/MonitorConfigurationTest.cs @@ -15,10 +15,14 @@ public void TestMonitorCapability_D1_1() { // Dell U2415 var source = @"(prot(monitor)type(LCD)model(U2415)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(04 0B 05 06 08 09 0C) 16 18 1A 52 60(0F 10 11 12) AA(01 02 04) AC AE B2 B6 C6 C8 C9 D6(01 04 05) DC(00 02 03 05) DF E0 E1 E2(00 01 02 04 14 19 0C 0D 0F 10 11 13) F0(00 08) F1(01 02) F2 FD)mswhql(1)asset_eep(40)mccs_ver(2.1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 6, 8, 9)); } [TestMethod] @@ -26,10 +30,14 @@ public void TestMonitorCapability_D1_2() { // Dell E1715S var source = @"(prot(monitor)type(LCD)model(E1715S)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 06 08 10 12 14(05 08 0B 0C) 16 18 1A 52 60(01 0F) AA AC AE B2 B6 C6 C8 C9 D6(01 04 05) DC(00 02 03 05) DF E0 E1 E2(00 01 02 04 06 0E 12 14) F0(00 01) F1(01) F2(00 01 02) FD)mswhql(1)asset_eep(40)mccs_ver(2.1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -37,10 +45,14 @@ public void TestMonitorCapability_D1_3() { // Dell SE2717H var source = @"(prot(monitor)type(LCD)model(SE2717H)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(04 05 08 0B 0C) 16 18 1A 52 60(01 11 ) AC AE B2 B6 C6 C8 C9 CC(02 0A 03 04 08 09 0D 06 ) D6(01 04 05) DC(00 02 03 05 ) DF E0 E1 E2(00 1D 01 02 22 20 21 0E 12 14) E3 F0(0C 0F 10 11 ) F1 F2 FD)mswhql(1)asset_eep(40)mccs_ver(2.1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 8)); } [TestMethod] @@ -48,10 +60,14 @@ public void TestMonitorCapability_D1_4() { // Dell U2720QM var source = @"(prot(monitor)type(lcd)model(U2720QM)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(01 04 05 06 08 09 0B 0C) 16 18 1A 52 60(11 1B 0F) AA(01 02 03 04) AC AE B2 B6 C6 C8 C9 CC(02 03 04 06 09 0A 0D 0E) D6(01 04 05) DC(00 03 05) DF E0 E1 E2(00 02 04 0B 0C 0D 0F 10 11 13 14 1B 1D 23 24 27 3A) EA F0(00 05 06 0A 0C 31 32 34 36) F1 F2 FD)mccs_ver(2.1)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 6, 8, 9)); } [TestMethod] @@ -59,10 +75,14 @@ public void TestMonitorCapability_D1_5() { // Dell S2721QS var source = @"(prot(monitor)type(lcd)model(S2721QS)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(05 08 0B 0C) 16 18 1A 52 60( 0F 11 12) 62 AC AE B2 B6 C6 C8 C9 CC(02 03 04 06 09 0A 0D 0E) D6(01 04 05) DC(00 03 ) DF E0 E1 E2(00 1D 02 20 21 22 0E 12 14 23 24 27 )E3 E5 E8 E9(00 01 02 21 22 24) EA F0(00 0C 0F 10 11 31 32 34 35) F1 F2 FD)mccs_ver(2.1)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -70,10 +90,14 @@ public void TestMonitorCapability_D1_6() { // DELL U2419H var source = @"(prot(monitor)type(LCD)model(U2419H)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(04 05 06 08 09 0B 0C) 16 18 1A 52 60(0F 11 ) AA(01 02 03 04 ) AC AE B2 B6 C6 C8 C9 CC(02 0A 03 04 08 09 0D 06 ) D6(01 04 05) DC(00 03 05 ) DF E0 E1 E2(00 1D 29 02 04 0C 0D 0F 10 11 13 14 ) E3(16 17 18 19 1A) F0(0C 12 ) F1 F2 FD)mswhql(1)asset_eep(40)mccs_ver(2.1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 6, 8, 9)); } [TestMethod] @@ -81,10 +105,14 @@ public void TestMonitorCapability_D1_7() { // DELL P2719H var source = @"(prot(monitor)type(lcd)model(P2719H)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(05 08 0B 0C) 16 18 1A 52 60(01 0F 11) AA(01 02 04) AC AE B2 B6 C6 C8 C9 CC(02 03 04 06 09 0a 0d 0e) D6(01 04 05) DC(00 03 05 ) DF E0 E1 E2(00 02 04 0E 12 14 1D) F0(00 0C) F1 F2 FD)mccs_ver(2.1)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -92,10 +120,14 @@ public void TestMonitorCapability_H1_1() { // HP LE1711 var source = @"(prot(monitor)type(lcd)model(HP LE1711)cmds(01 02 03 07 0C 4E F3 E3)vcp(02 04 05 06 08 0B 0C 0E 10 12 14(01 05 08 0B) 16 18 1A 1E 1F 20 30 3E 52 60(01) 6C 6E 70 AC AE B6 C0 C6 C8 C9 CA CC(01 02 03 04 05 06 08 0A 0D 14) D6(01 04 05) DF FA(00 01 02) FB FC FD FE(00 01 02 04) )mswhql(1)mccs_ver(2.1)asset_eep(32)mpu_ver(01))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -103,10 +135,14 @@ public void TestMonitorCapability_N1_1() { // NEC L220W var source = @"(vcp(02 04 05 06 08 0E 10 12 14(01 02 06 08 0B 0E) 16 18 1A 1E 20 30 3E 68(01 02 03 04 05 06 07 09 0D) B0 B6 DF E3 F4 F5(01 02 03 04 05 06 07 09 0D) F9 FA FC FF)vcp_p02(33 37 47 52 64 65 DA EA FF)vcp_p10(10 11 26 27 28 29 2A 2B 2C 2D)prot(monitor)type(LCD)cmds(01 02 03 07 0C C2 C4 C6 C8 F3)mccs_ver(2.0)asset_eep(20)mpu_ver(1.02)model(L220W)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 2, 6, 8)); } [TestMethod] @@ -114,10 +150,14 @@ public void TestMonitorCapability_L1_1() { // LG MP57 var source = @"(prot(monitor)type(lcd)model(MP57)cmds(01 02 03 0C E3 F3)vcp(02030405080B0C101214(01 05 06 07 08 0B) 15(10 11 20 30 40 0B)16181A5260(01 03 04)6C6E7087ACAEB6C0C6C8C9D6(01 04)DFE0E1E3(00 01 02 03 04 10 11 12 13 14)ECEFFD(00 01)FE(00 01 02)FF)mswhql(1)mccs_ver(2.1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 6, 7, 8)); } [TestMethod] @@ -125,10 +165,14 @@ public void TestMonitorCapability_L1_2() { // LG 27UL550-W var source = @"(prot(monitor)type(lcd)UL550_500cmds(01 02 03 0C E3 F3)vcp(02 04 05 08 10 12 14(05 08 0B) 16 18 1A 52 60(11 12 0F 10) AC AE B2 B6 C0 C6 C8 C9 D6(01 04) DF 62 8D F4 F5(00 01 02) F6(00 01 02) 4D 4E 4F 15(01 06 09 10 11 13 14 28 29 32 44 48) F7(00 01 02 03) F8(00 01) F9 EF FD(00 01) FE(00 01 02) FF)mccs_ver(2.1)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -136,10 +180,14 @@ public void TestMonitorCapability_L1_3() { // LG GP850 var source = @"(prot(monitor)type(lcd)model(GP850)cmds(01 02 03 0C E3 F3)vcp(02 04 05 08 10 12 14(05 08 0B ) 16 18 1A 52 60(11 12 0F 10 ) AC AE B2 B6 C0 C6 C8 C9 D6(01 04) DF 62 8D F4 F5(01 02 03 04) F6(00 01 02) 4D 4E 4F 15(01 06 11 13 14 15 18 19 20 22 23 24 28 29 32 48) F7(00 01 02 03) F8(00 01) F9 EF FA(00 01) FD(00 01) FE(00 01 02) FF)mccs_ver(2.1)mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 8)); } [TestMethod] @@ -147,10 +195,14 @@ public void TestMonitorCapability_L2_1() { // Lenovo P27u-10 var source = @"(prot(monitor)type(LCD)model(P27u_10)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(01 02 05 06 08 0B 0C 0D 0E 0F) 16 18 1A 52 60(0F 11 12 13) 86(02 05) AC AE B2 B6 C6 C8 C9 CA(01 02) CC(02 03 04 05 06 09 0A 0D) D6(01 04 05) DC(00 01 02 03 04 05 06) DF E0(00 01 02) EA(00 01) EB(00 01) )mswhql(1)asset_eep(40)mccs_ver(2.2))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 2, 5, 6, 8)); } [TestMethod] @@ -158,10 +210,14 @@ public void TestMonitorCapability_L2_2() { // Lenovo P27h-10 var source = @"(prot(monitor)type(LCD)model(P27h_10)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 06 08 10 12 14(01 05 06 08 0B) 16 18 1A 52 60(0F 10 11 12) AC AE B2 B6 C6 C8 CA CC(02 03 04 05 06 09 0A 0D) D6(01 04 05) DF FD)mswhql(1)asset_eep(40)mccs_ver(2.2))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 6, 8)); } [TestMethod] @@ -169,10 +225,14 @@ public void TestMonitorCapability_L2_3() { // Lenovo Q24i-10 var source = @"(prot(monitor)type(LCD)model(Q24i-10)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 06 08 10 12 14(01 05 06 08 0B) 16 18 1A 52 60(01 11) 62 AC AE B2 B6 C6 C8 C9 CA CC(02 03 04 05 06 09 0A 0D) D6(01 04 05) DF)mswhql(1)asset_eep(40)mccs_ver(2.2))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 6, 8)); } [TestMethod] @@ -180,10 +240,14 @@ public void TestMonitorCapability_B1_1() { // BenQ GW2270H var source = @"(prot(monitor)type(LCD)model(GW2270H)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 0B 0C 10 12 14(04 05 08 0B) 16 18 1A 52 60(01 11 12) 62 72(50 64 78 8C A0) 86(02 05) 87 8A 8D(01 02) 90 AA(01 02 FF) AC AE B2 B5(00 01) B6 C0 C6 C8 C9 CA(01 02) CC(01 02 03 04 05 06 09 0A 0B 0D 0E 12 14 1A 1E 1F 20) DA(00 02) D6(01 05) DC(00 03 05 0B 0C 0E 12 13) DF EA(00 01 02 03 04 05) EE(00 01) EF(00 01) F0(00 01 02) F1(00 01) F2(14 28 3C 50 64) F4(00 01) F5(00 01) F7(00 01) F8(00 0A 14 1E) FC(00 01))mswhql(1)asset_eep(40)mccs_ver(2.2)mpu(1.02))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 8)); } [TestMethod] @@ -191,10 +255,14 @@ public void TestMonitorCapability_B1_2() { // BenQ XL2411P var source = @"(prot(monitor)type(lcd)model(XL2411P)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 0B 0C 10 12 14(04 05 08 0B) 16 18 1A 52 60(03 0F 11) 62 8D(01 02) AC AE B2 B6 C0 C6 C8 C9 CA(01 02) CC(01 02 03 04 05 06 07 08 09 0A 0B 0D 0F 12 14 1A 1E 1F 20) D6(01 05) DF 86(01 02 05 0B 0C 0D 0E 0F 10 11 12 13) F7(00 01) DA(00 02) DC(00 03 0B 0C 0E 15 16 17 18 19 1A) EA(00 01 02 03 04 05) F4(00 01) 87 90 8A 72(50 64 78 8C A0) F8(00 0A 14 1E) EF(00 01) F0(00 01 02) )mswhql(1)mccs_ver(2.2))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 4, 5, 8)); } [TestMethod] @@ -202,10 +270,14 @@ public void TestMonitorCapability_A1_1() { // ASUS PB277Q var source = @"(prot(monitor)type(lcd)model(PB277Q)cmds(01 02 03 07 0C 4E F3 E3)vcp(02 04 05 08 0B 0C 10 12 14(05 06 08 0B) 16 18 1A 6C 6E 70 AC AE B6 C0 C6 C8 C9 CC(00 01 02 03 04 05 06 07 08 09 0A 0C 0D 11 12 14 1A 1E 1F 72 23 73) D6(01 05) DF 60(01 03 11 0F) 62 8D )mswhql(1)mccs_ver(2.1)asset_eep(32)mpu_ver(01))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 6, 8)); } [TestMethod] @@ -213,10 +285,14 @@ public void TestMonitorCapability_A2_1() { // Acer XB271HU var source = @"(prot(monitor)type(lcd)model(XB271HU)cmds(01 02 03 06 07 0C E3 F3)vcp(02 03(01) 04 05 08 10 12 14(03 05 09 0B) 16 18 1A 2E 52 59 5A 5B 5C 5D 5E 72(00 78 FF) 8A AC AE B6 C0 C8 C9 CA DF)mccs_ver(2.2)vcpname(10(Brightness))mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 3, 5, 9)); } [TestMethod] @@ -224,10 +300,14 @@ public void TestMonitorCapability_A3_1() { // AOC G2460PG var source = @"(prot(monitor)type(lcd)model(G2460PG)cmds(01 02 03 06 07 0C E3 F3)vcp(02 03(01) 04 05 08 10 12 14(03 05 09 0B) 16 18 1A 2E 52 59 5A 5B 5C 5D 5E 72(00 78 FF) 8A AC AE B6 C0 C8 C9 CA DF)mccs_ver(2.2)vcpname(10(Brightness))mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 3, 5, 9)); } [TestMethod] @@ -235,10 +315,14 @@ public void TestMonitorCapability_A3_2() { // AOC AG271QG var source = @"(prot(monitor)type(lcd)model(AG271QG)cmds(01 02 03 06 07 0C E3 F3)vcp(02 03(01) 04 05 08 10 12 14(03 05 09 0B) 16 18 1A 2E 52 59 5A 5B 5C 5D 5E 72(00 78 FF) 8A AC AE B6 C0 C8 C9 CA DF)mccs_ver(2.2)vcpname(10(Brightness))mswhql(1))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsFalse(speakerVolume); + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 3, 5, 9)); } [TestMethod] @@ -246,10 +330,14 @@ public void TestMonitorCapability_A3_3() { // AOC G2590FX var source = @"(prot(monitor)type(lcd)model(G2590FX)cmds(01 02 03 07 0C E3 F3)vcp(02 04 05 08 10 12 14(01 05 06 08 0B) 16 18 1A 52 60(01 11 12 0F) 62 8D(01 02) AC AE B6 C0 C6 C8 C9 CA(01 02) CC(02 03 04 05 07 08 09 0A 0D 01 06 0B 12 14 16 1E) 86(01 02 05 0B 0C 0F 10 11) D6(01 05) DC(00 0B 0C 0D 0E 0F 10) DF FF)mswhql(1)asset_eep(40)mccs_ver(2.2)))"; - var (brightness, contrast, speakerVolume) = TestMonitorCapabilityBase(source); + var (brightness, contrast, speakerVolume) = TestEnumerateVcpCodes(source); Assert.IsTrue(brightness); Assert.IsTrue(contrast); Assert.IsTrue(speakerVolume); // True + + var (temperature, values) = TestGetVcpCodeValues(source); + Assert.IsTrue(temperature); + Assert.IsTrue(AreIncluded(values, 5, 6, 8)); } private enum VcpCode : byte @@ -257,11 +345,12 @@ private enum VcpCode : byte None = 0x0, Luminance = 0x10, Contrast = 0x12, + Temperature = 0x14, SpeakerVolume = 0x62, PowerMode = 0xD6, } - private static (bool brightness, bool contrast, bool speakerVolume) TestMonitorCapabilityBase(string source) + private static (bool brightness, bool contrast, bool speakerVolume) TestEnumerateVcpCodes(string source) { var @class = new PrivateType(typeof(MonitorConfiguration)); var enumerator = @class.InvokeStatic("EnumerateVcpCodes", source) as IEnumerable; @@ -274,5 +363,20 @@ private static (bool brightness, bool contrast, bool speakerVolume) TestMonitorC contrast: vcpCodes.Contains((byte)VcpCode.Contrast), speakerVolume: vcpCodes.Contains((byte)VcpCode.SpeakerVolume)); } + + private static (bool temperature, byte[] values) TestGetVcpCodeValues(string source) + { + var @class = new PrivateType(typeof(MonitorConfiguration)); + var vcpCodes = @class.InvokeStatic("GetVcpCodeValues", source) as Dictionary; + + return vcpCodes.TryGetValue((byte)VcpCode.Temperature, out byte[] values) + ? (true, values) + : (false, Array.Empty()); + } + + private static bool AreIncluded(T[] source, params T[] elements) + { + return source.Intersect(elements).Count() == elements.Length; + } } } \ No newline at end of file From 66cbd7adc517e9ec40d6dd216e5737c05742e31c Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 12 Mar 2023 07:18:01 +0900 Subject: [PATCH 11/13] Refactor --- .../DisplayInformation.cs | 2 +- .../Monitorian.Supplement/LightInformation.cs | 2 +- Source/Monitorian.Supplement/UIInformation.cs | 2 +- .../ScreenFrame/LogicalTreeHelperAddition.cs | 8 ++++---- Source/ScreenFrame/VisualTreeHelperAddition.cs | 18 +++++++++--------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/Monitorian.Supplement/DisplayInformation.cs b/Source/Monitorian.Supplement/DisplayInformation.cs index 49d67d45..504d5526 100644 --- a/Source/Monitorian.Supplement/DisplayInformation.cs +++ b/Source/Monitorian.Supplement/DisplayInformation.cs @@ -16,7 +16,7 @@ namespace Monitorian.Supplement /// is only available /// on Windows 10 (version 10.0.17134.0) or newer. /// - public class DisplayInformation + public static class DisplayInformation { #region Type diff --git a/Source/Monitorian.Supplement/LightInformation.cs b/Source/Monitorian.Supplement/LightInformation.cs index f7b52d0f..89820cc6 100644 --- a/Source/Monitorian.Supplement/LightInformation.cs +++ b/Source/Monitorian.Supplement/LightInformation.cs @@ -14,7 +14,7 @@ namespace Monitorian.Supplement /// has been available /// since Windows 8.1 but is officially supported on Windows 10 (version 10.0.10240.0) or newer. /// - public class LightInformation + public static class LightInformation { /// /// Determines whether an integrated ambient light sensor exists. diff --git a/Source/Monitorian.Supplement/UIInformation.cs b/Source/Monitorian.Supplement/UIInformation.cs index 07f65ed6..f46e90ef 100644 --- a/Source/Monitorian.Supplement/UIInformation.cs +++ b/Source/Monitorian.Supplement/UIInformation.cs @@ -15,7 +15,7 @@ namespace Monitorian.Supplement /// is available /// on Windows 10 (version 10.0.10240.0) or newer. /// - public class UIInformation + public static class UIInformation { private static UISettings _uiSettings; diff --git a/Source/ScreenFrame/LogicalTreeHelperAddition.cs b/Source/ScreenFrame/LogicalTreeHelperAddition.cs index 165fae06..bec5d06f 100644 --- a/Source/ScreenFrame/LogicalTreeHelperAddition.cs +++ b/Source/ScreenFrame/LogicalTreeHelperAddition.cs @@ -15,15 +15,15 @@ public static class LogicalTreeHelperAddition /// /// Enumerates descendant objects of a specified object. /// - /// Type of descendant object - /// Ancestor object + /// Type of descendant dependency object + /// Ancestor dependency object /// Enumerable collection of descendant objects - public static IEnumerable EnumerateDescendants(DependencyObject reference) + public static IEnumerable EnumerateDescendants(DependencyObject reference) where T : DependencyObject { if (reference is null) yield break; - foreach (var child in LogicalTreeHelper.GetChildren(reference).OfType()) + foreach (DependencyObject child in LogicalTreeHelper.GetChildren(reference).OfType()) { if (child is T buffer) yield return buffer; diff --git a/Source/ScreenFrame/VisualTreeHelperAddition.cs b/Source/ScreenFrame/VisualTreeHelperAddition.cs index 87244b00..a77fcd6e 100644 --- a/Source/ScreenFrame/VisualTreeHelperAddition.cs +++ b/Source/ScreenFrame/VisualTreeHelperAddition.cs @@ -231,11 +231,11 @@ public static Rect ConvertToRect(IntPtr lParam) #region VisualTree /// - /// Attempts to get the first ancestor visual of a specified visual. + /// Attempts to get the first ancestor object of a specified object. /// - /// Type of ancestor visual - /// Descendant visual - /// Ancestor visual + /// Type of ancestor dependency object + /// Descendant dependency object + /// Ancestor dependency object /// True if successfully gets public static bool TryGetAncestor(DependencyObject reference, out T ancestor) where T : DependencyObject { @@ -256,11 +256,11 @@ public static bool TryGetAncestor(DependencyObject reference, out T ancestor) } /// - /// Attempts to get the first descendant visual of a specified visual. + /// Attempts to get the first descendant object of a specified object. /// - /// Type of descendant visual - /// Ancestor visual - /// Descendant visual + /// Type of descendant dependency object + /// Ancestor dependency object + /// Descendant dependency object /// True if successfully gets public static bool TryGetDescendant(DependencyObject reference, out T descendant) where T : DependencyObject { @@ -269,7 +269,7 @@ public static bool TryGetDescendant(DependencyObject reference, out T descend while (parent is not null) { - var count = VisualTreeHelper.GetChildrenCount(parent); + int count = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count; i++) { var child = VisualTreeHelper.GetChild(parent, i); From 07662eff1d90eb0d397214ad0ff3e4145ce4566d Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 12 Mar 2023 23:44:12 +0900 Subject: [PATCH 12/13] Enable to change color temperature --- .../Helper/EnumerableExtension.cs | 14 ++ .../Models/Monitor/DdcMonitorItem.cs | 25 ++++ .../Models/Monitor/IMonitor.cs | 3 + .../Models/Monitor/MonitorConfiguration.cs | 120 +++++++++++++----- .../Models/Monitor/MonitorItem.cs | 4 + .../ViewModels/MonitorViewModel.cs | 12 ++ 6 files changed, 149 insertions(+), 29 deletions(-) diff --git a/Source/Monitorian.Core/Helper/EnumerableExtension.cs b/Source/Monitorian.Core/Helper/EnumerableExtension.cs index 6e54e8ce..0aaa7d4f 100644 --- a/Source/Monitorian.Core/Helper/EnumerableExtension.cs +++ b/Source/Monitorian.Core/Helper/EnumerableExtension.cs @@ -55,5 +55,19 @@ public static IEnumerable Split(this IEnumerable so yield return buffer.ToArray(); } } + + public static IEnumerable Clip(this IEnumerable source, TSource start, TSource end) where TSource : IComparable + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + // Remove the elements before the start and after the end while keeping the elements + // between intact. + return source + .SkipWhile(x => x.CompareTo(start) < 0) + .Reverse() + .SkipWhile(x => x.CompareTo(end) > 0) + .Reverse(); + } } } \ No newline at end of file diff --git a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs index da0f5ba1..72a352fe 100644 --- a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -19,6 +20,7 @@ internal class DdcMonitorItem : MonitorItem public override bool IsBrightnessSupported => _capability.IsBrightnessSupported; public override bool IsContrastSupported => _capability.IsContrastSupported; public override bool IsPrecleared => _capability.IsPrecleared; + public override bool IsTemperatureSupported => _capability.IsTemperatureSupported; public DdcMonitorItem( string deviceInstanceId, @@ -112,6 +114,29 @@ public override AccessResult SetContrast(int contrast) return result; } + public override AccessResult ChangeTemperature() + { + var (result, current) = MonitorConfiguration.GetTemperature(_handle); + if (result.Status == AccessStatus.Succeeded) + { + var next = GetNext(_capability.Temperatures, current); + result = MonitorConfiguration.SetTemperature(_handle, next); + + Debug.WriteLine($"Color Temperature: {current} -> {next}"); + } + return result; + + static byte GetNext(IReadOnlyList source, byte current) + { + for (int i = 0; i < source.Count; i++) + { + if (source[i] == current) + return (i < source.Count - 1) ? source[i + 1] : source[0]; + } + return source.First(); // Fallback + } + } + #region IDisposable private bool _isDisposed = false; diff --git a/Source/Monitorian.Core/Models/Monitor/IMonitor.cs b/Source/Monitorian.Core/Models/Monitor/IMonitor.cs index dbcc86ee..d9858ae2 100644 --- a/Source/Monitorian.Core/Models/Monitor/IMonitor.cs +++ b/Source/Monitorian.Core/Models/Monitor/IMonitor.cs @@ -18,6 +18,7 @@ public interface IMonitor : IDisposable bool IsReachable { get; } bool IsBrightnessSupported { get; } bool IsContrastSupported { get; } + bool IsTemperatureSupported { get; } int Brightness { get; } int BrightnessSystemAdjusted { get; } @@ -29,6 +30,8 @@ public interface IMonitor : IDisposable AccessResult UpdateContrast(); AccessResult SetContrast(int contrast); + + AccessResult ChangeTemperature(); } public enum AccessStatus diff --git a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs index eb8bb6ba..2b378914 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs @@ -8,6 +8,8 @@ using System.Text; using System.Threading.Tasks; +using Monitorian.Core.Helper; + namespace Monitorian.Core.Models.Monitor { /// @@ -262,15 +264,16 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle capabilitiesStringLength)) { var capabilitiesString = buffer.ToString(); - IReadOnlyCollection vcpCodes = EnumerateVcpCodes(capabilitiesString).ToArray(); + IReadOnlyDictionary vcpCodeValues = GetVcpCodeValues(capabilitiesString); return new MonitorCapability( isHighLevelBrightnessSupported: isHighLevelSupported, - isLowLevelBrightnessSupported: vcpCodes.Contains((byte)VcpCode.Luminance), - isContrastSupported: vcpCodes.Contains((byte)VcpCode.Contrast), + isLowLevelBrightnessSupported: vcpCodeValues.ContainsKey((byte)VcpCode.Luminance), + isContrastSupported: vcpCodeValues.ContainsKey((byte)VcpCode.Contrast), + temperatures: (vcpCodeValues.TryGetValue((byte)VcpCode.Temperature, out byte[] values) ? values : null), capabilitiesString: (verbose ? capabilitiesString : null), - capabilitiesReport: (verbose ? MakeCapabilitiesReport(capabilitiesString) : null), - capabilitiesData: (verbose && !vcpCodes.Any() ? GetCapabilitiesData(physicalMonitorHandle, capabilitiesStringLength) : null)); + capabilitiesReport: (verbose ? MakeCapabilitiesReport(vcpCodeValues) : null), + capabilitiesData: (verbose && !vcpCodeValues.Any() ? GetCapabilitiesData(physicalMonitorHandle, capabilitiesStringLength) : null)); } } return new MonitorCapability( @@ -278,13 +281,12 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle isLowLevelBrightnessSupported: false, isContrastSupported: false); - static string MakeCapabilitiesReport(string capabilitiesString) + static string MakeCapabilitiesReport(IReadOnlyDictionary vcpCodeValues) { - IReadOnlyDictionary vcpCodeValues = GetVcpCodeValues(capabilitiesString); - var temperatureString = vcpCodeValues.TryGetValue((byte)VcpCode.Temperature, out byte[] values) - ? $"{true} ({values?.Intersect(new byte[] { 3, 4, 5, 6, 7, 8, 9, 10 }).Count() ?? 0})" - : false.ToString(); + && (values is { Length: > 0 }) + ? $"{true} ({string.Join(" ", values)})" + : false.ToString(); return $"Luminance: {vcpCodeValues.ContainsKey((byte)VcpCode.Luminance)}, " + $"Contrast: {vcpCodeValues.ContainsKey((byte)VcpCode.Contrast)}, " + @@ -510,6 +512,20 @@ public static (AccessResult result, uint minimum, uint current, uint maximum) Ge return GetVcpValue(physicalMonitorHandle, VcpCode.Contrast); } + /// + /// Gets raw color temperature. + /// + /// Physical monitor handle + /// + /// result: Result + /// current: Raw current color temperature + /// + public static (AccessResult result, byte current) GetTemperature(SafePhysicalMonitorHandle physicalMonitorHandle) + { + var (result, _, current, _) = GetVcpValue(physicalMonitorHandle, VcpCode.Temperature); + return (result, (byte)current); + } + private static (AccessResult result, uint minimum, uint current, uint maximum) GetVcpValue(SafePhysicalMonitorHandle physicalMonitorHandle, VcpCode vcpCode) { if (!EnsurePhysicalMonitorHandle(physicalMonitorHandle)) @@ -578,6 +594,17 @@ public static AccessResult SetContrast(SafePhysicalMonitorHandle physicalMonitor return SetVcpValue(physicalMonitorHandle, VcpCode.Contrast, contrast); } + /// + /// Sets raw color temperature. + /// + /// Physical monitor handle + /// Raw color temperature + /// + public static AccessResult SetTemperature(SafePhysicalMonitorHandle physicalMonitorHandle, byte temperature) + { + return SetVcpValue(physicalMonitorHandle, VcpCode.Temperature, temperature); + } + private static AccessResult SetVcpValue(SafePhysicalMonitorHandle physicalMonitorHandle, VcpCode vcpCode, uint value) { if (!EnsurePhysicalMonitorHandle(physicalMonitorHandle)) @@ -668,47 +695,81 @@ internal class MonitorCapability [DataMember(Order = 3)] public bool IsPrecleared { get; } - [DataMember(Order = 4)] - public string CapabilitiesString { get; } + /// + /// Supported color temperatures + /// + /// + /// The following temperatures are defined. + /// 3: 4000° K + /// 4: 5000° K + /// 5: 6500° K + /// 6: 7500° K + /// 7: 8200° K + /// 8: 9300° K + /// 9: 10000° K + /// 10: 11500° K + /// Not all temperatures are supported in practice. + /// An additional temperature can be inserted depending on a specific model. + /// + public IReadOnlyList Temperatures { get; } + + public bool IsTemperatureSupported => (Temperatures is { Count: > 0 }); + [DataMember(Order = 4, Name = nameof(IsTemperatureSupported))] + private string _isTemperatureSupportedString; [DataMember(Order = 5)] - public string CapabilitiesReport { get; } + public string CapabilitiesString { get; } [DataMember(Order = 6)] + public string CapabilitiesReport { get; } + + [DataMember(Order = 7)] public string CapabilitiesData { get; } + [OnSerializing] + private void OnSerializing(StreamingContext context) + { + _isTemperatureSupportedString = IsTemperatureSupported + ? $"true ({string.Join(" ", Temperatures)})" + : "false"; + } + public MonitorCapability( bool isHighLevelBrightnessSupported, bool isLowLevelBrightnessSupported, bool isContrastSupported, + IReadOnlyList temperatures = null, string capabilitiesString = null, string capabilitiesReport = null, - byte[] capabilitiesData = null) - { - this.IsHighLevelBrightnessSupported = isHighLevelBrightnessSupported; - this.IsLowLevelBrightnessSupported = isLowLevelBrightnessSupported; - this.IsContrastSupported = isContrastSupported; - this.CapabilitiesString = capabilitiesString; - this.CapabilitiesReport = capabilitiesReport; - this.CapabilitiesData = (capabilitiesData is not null) ? Convert.ToBase64String(capabilitiesData) : null; - } + byte[] capabilitiesData = null) : this( + isHighLevelBrightnessSupported: isHighLevelBrightnessSupported, + isLowLevelBrightnessSupported: isLowLevelBrightnessSupported, + isContrastSupported: isContrastSupported, + isPrecleared: false, + temperatures: temperatures, + capabilitiesString: capabilitiesString, + capabilitiesReport: capabilitiesReport, + capabilitiesData: capabilitiesData) + { } private MonitorCapability( bool isHighLevelBrightnessSupported, bool isLowLevelBrightnessSupported, bool isContrastSupported, bool isPrecleared, + IReadOnlyList temperatures, string capabilitiesString, string capabilitiesReport, - byte[] capabilitiesData) : this( - isHighLevelBrightnessSupported: isHighLevelBrightnessSupported, - isLowLevelBrightnessSupported: isLowLevelBrightnessSupported, - isContrastSupported: isContrastSupported, - capabilitiesString: capabilitiesString, - capabilitiesReport: capabilitiesReport, - capabilitiesData: capabilitiesData) + byte[] capabilitiesData) { + this.IsHighLevelBrightnessSupported = isHighLevelBrightnessSupported; + this.IsLowLevelBrightnessSupported = isLowLevelBrightnessSupported; + this.IsContrastSupported = isContrastSupported; this.IsPrecleared = isPrecleared; + this.Temperatures = temperatures?.Clip(3, 10).ToArray(); // 3 is warmest and 10 is coldest. + this.CapabilitiesString = capabilitiesString; + this.CapabilitiesReport = capabilitiesReport; + this.CapabilitiesData = (capabilitiesData is not null) ? Convert.ToBase64String(capabilitiesData) : null; } public static MonitorCapability PreclearedCapability => _preclearedCapability.Value; @@ -717,6 +778,7 @@ private MonitorCapability( isLowLevelBrightnessSupported: true, isContrastSupported: true, isPrecleared: true, + temperatures: null, capabilitiesString: null, capabilitiesReport: null, capabilitiesData: null)); diff --git a/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs b/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs index d7c9b9ca..1d880322 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs @@ -22,6 +22,7 @@ internal abstract class MonitorItem : IMonitor, IDisposable public virtual bool IsBrightnessSupported => IsReachable; public virtual bool IsContrastSupported => false; public virtual bool IsPrecleared => false; + public virtual bool IsTemperatureSupported => false; public MonitorItem( string deviceInstanceId, @@ -57,6 +58,8 @@ public MonitorItem( public virtual AccessResult UpdateContrast() => AccessResult.NotSupported; public virtual AccessResult SetContrast(int contrast) => AccessResult.NotSupported; + public virtual AccessResult ChangeTemperature() => AccessResult.NotSupported; + public override string ToString() { return SimpleSerialization.Serialize( @@ -71,6 +74,7 @@ public override string ToString() (nameof(IsBrightnessSupported), IsBrightnessSupported), (nameof(IsContrastSupported), IsContrastSupported), (nameof(IsPrecleared), IsPrecleared), + (nameof(IsTemperatureSupported), IsTemperatureSupported), (nameof(Brightness), Brightness), (nameof(BrightnessSystemAdjusted), BrightnessSystemAdjusted), (nameof(Contrast), Contrast)); diff --git a/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs b/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs index e3423d46..218325d0 100644 --- a/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs +++ b/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs @@ -391,6 +391,18 @@ private bool SetContrast(int contrast) #endregion + #region Temperature + + public bool IsTemperatureSupported => _monitor.IsTemperatureSupported; + + public void ChangeTemperature() + { + if (IsTemperatureSupported) + _monitor.ChangeTemperature(); + } + + #endregion + #region Controllable public bool IsReachable => _monitor.IsReachable; From 557cd28af1f05b4e9e36189a7b900e5db3504b8b Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 12 Mar 2023 23:47:45 +0900 Subject: [PATCH 13/13] Increment minor version --- Source/Installer/Product.wxs | 2 +- Source/Monitorian.Core/Properties/AssemblyInfo.cs | 4 ++-- Source/Monitorian.Supplement/Properties/AssemblyInfo.cs | 4 ++-- Source/Monitorian.Test/Properties/AssemblyInfo.cs | 4 ++-- Source/Monitorian/Properties/AssemblyInfo.cs | 4 ++-- Source/ScreenFrame/Properties/AssemblyInfo.cs | 4 ++-- Source/StartupAgency/Properties/AssemblyInfo.cs | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/Installer/Product.wxs b/Source/Installer/Product.wxs index 8e129cbf..5db2666a 100644 --- a/Source/Installer/Product.wxs +++ b/Source/Installer/Product.wxs @@ -1,6 +1,6 @@  -