From 698844a38c3bee1e0adb6f54a06ba5ca0bc18995 Mon Sep 17 00:00:00 2001 From: emoacht Date: Sun, 14 May 2023 13:59:21 +0900 Subject: [PATCH 1/3] Change subscribing only when internal monitor exists --- Source/Monitorian.Core/AppControllerCore.cs | 13 ++++++++----- .../Monitorian.Core/ViewModels/MonitorViewModel.cs | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Monitorian.Core/AppControllerCore.cs b/Source/Monitorian.Core/AppControllerCore.cs index 22f5716b..588b2f7d 100644 --- a/Source/Monitorian.Core/AppControllerCore.cs +++ b/Source/Monitorian.Core/AppControllerCore.cs @@ -94,12 +94,15 @@ public virtual async Task InitiateAsync() _sessionWatcher.Subscribe((e) => OnMonitorsChangeInferred(nameof(SessionWatcher), e)); _powerWatcher.Subscribe((e) => OnMonitorsChangeInferred(nameof(PowerWatcher), e)); - _brightnessWatcher.Subscribe((instanceName, brightness) => + if (Monitors.Any(x => x.IsInternal)) { - if (!_sessionWatcher.IsLocked) - Update(instanceName, brightness); - }, - async (message) => await Recorder.RecordAsync(message)); + _brightnessWatcher.Subscribe((instanceName, brightness) => + { + if (!_sessionWatcher.IsLocked) + Update(instanceName, brightness); + }, + async (message) => await Recorder.RecordAsync(message)); + } if (_brightnessConnector.CanConnect) { diff --git a/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs b/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs index d388ac03..a8365359 100644 --- a/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs +++ b/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs @@ -52,6 +52,7 @@ internal void Replace(IMonitor monitor) public byte DisplayIndex => _monitor.DisplayIndex; public byte MonitorIndex => _monitor.MonitorIndex; public Rect MonitorRect => _monitor.MonitorRect; + public bool IsInternal => _monitor.IsInternal; #region Customization @@ -540,8 +541,6 @@ static ulong GetTopLeft(Point location) #endregion - public bool IsInternal => _monitor.IsInternal; - public bool IsTarget { get => _isTarget; From 1412f1cde279849795e8b4786637f4b267dc3efb Mon Sep 17 00:00:00 2001 From: emoacht Date: Mon, 15 May 2023 12:16:29 +0900 Subject: [PATCH 2/3] Change methods for non-conitunous value --- .../Models/Monitor/DdcMonitorItem.cs | 16 ++- .../Models/Monitor/IMonitor.cs | 3 +- .../Models/Monitor/MonitorConfiguration.cs | 132 ++++++++---------- .../Models/Monitor/MonitorItem.cs | 4 +- .../ViewModels/MonitorViewModel.cs | 33 ++++- 5 files changed, 101 insertions(+), 87 deletions(-) diff --git a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs index 72a352fe..87e5f6dd 100644 --- a/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs @@ -20,7 +20,6 @@ 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, @@ -114,15 +113,20 @@ public override AccessResult SetContrast(int contrast) return result; } - public override AccessResult ChangeTemperature() + public override AccessResult ChangeValue(byte code, int value = -1) { - var (result, current) = MonitorConfiguration.GetTemperature(_handle); + if ((_capability.CapabilitiesCodes is null) + || !_capability.CapabilitiesCodes.TryGetValue(code, out var values) + || (values is not { Count: > 1 })) + return AccessResult.NotSupported; + + var (result, _, current, _) = MonitorConfiguration.GetValue(_handle, code); if (result.Status == AccessStatus.Succeeded) { - var next = GetNext(_capability.Temperatures, current); - result = MonitorConfiguration.SetTemperature(_handle, next); + var next = ((value >= 0) && values.Contains((byte)value)) ? (byte)value : GetNext(values, (byte)current); + result = MonitorConfiguration.SetValue(_handle, code, next); - Debug.WriteLine($"Color Temperature: {current} -> {next}"); + Debug.WriteLine($"Change {code:X2}: {(byte)current} -> {next}"); } return result; diff --git a/Source/Monitorian.Core/Models/Monitor/IMonitor.cs b/Source/Monitorian.Core/Models/Monitor/IMonitor.cs index d9858ae2..32824c17 100644 --- a/Source/Monitorian.Core/Models/Monitor/IMonitor.cs +++ b/Source/Monitorian.Core/Models/Monitor/IMonitor.cs @@ -18,7 +18,6 @@ public interface IMonitor : IDisposable bool IsReachable { get; } bool IsBrightnessSupported { get; } bool IsContrastSupported { get; } - bool IsTemperatureSupported { get; } int Brightness { get; } int BrightnessSystemAdjusted { get; } @@ -31,7 +30,7 @@ public interface IMonitor : IDisposable AccessResult UpdateContrast(); AccessResult SetContrast(int contrast); - AccessResult ChangeTemperature(); + AccessResult ChangeValue(byte code, int value = -1); } public enum AccessStatus diff --git a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs index 2b378914..d4f51033 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -188,11 +189,23 @@ private enum VcpCode : byte None = 0x0, Luminance = 0x10, Contrast = 0x12, - Temperature = 0x14, + Temperature = 0x14, // Select Color Preset + InputSource = 0x60, // Input Select SpeakerVolume = 0x62, PowerMode = 0xD6, } + private static bool TryConvertVcpCode(byte code, out VcpCode vcpCode) + { + if (Enum.IsDefined(typeof(VcpCode), code)) + { + vcpCode = (VcpCode)code; + return true; + } + vcpCode = default; + return false; + } + public static IEnumerable EnumeratePhysicalMonitors(IntPtr monitorHandle, bool verbose = false) { if (!GetNumberOfPhysicalMonitorsFromHMONITOR( @@ -264,16 +277,16 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle capabilitiesStringLength)) { var capabilitiesString = buffer.ToString(); - IReadOnlyDictionary vcpCodeValues = GetVcpCodeValues(capabilitiesString); + Dictionary vcpCodes = ExtractVcpCodes(capabilitiesString); return new MonitorCapability( isHighLevelBrightnessSupported: isHighLevelSupported, - isLowLevelBrightnessSupported: vcpCodeValues.ContainsKey((byte)VcpCode.Luminance), - isContrastSupported: vcpCodeValues.ContainsKey((byte)VcpCode.Contrast), - temperatures: (vcpCodeValues.TryGetValue((byte)VcpCode.Temperature, out byte[] values) ? values : null), + isLowLevelBrightnessSupported: vcpCodes.ContainsKey((byte)VcpCode.Luminance), + isContrastSupported: vcpCodes.ContainsKey((byte)VcpCode.Contrast), + capabilitiesCodes: FilterVcpCodes(vcpCodes), capabilitiesString: (verbose ? capabilitiesString : null), - capabilitiesReport: (verbose ? MakeCapabilitiesReport(vcpCodeValues) : null), - capabilitiesData: (verbose && !vcpCodeValues.Any() ? GetCapabilitiesData(physicalMonitorHandle, capabilitiesStringLength) : null)); + capabilitiesReport: (verbose ? MakeCapabilitiesReport(vcpCodes) : null), + capabilitiesData: (verbose && !vcpCodes.Any() ? GetCapabilitiesData(physicalMonitorHandle, capabilitiesStringLength) : null)); } } return new MonitorCapability( @@ -283,14 +296,10 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle static string MakeCapabilitiesReport(IReadOnlyDictionary vcpCodeValues) { - var temperatureString = vcpCodeValues.TryGetValue((byte)VcpCode.Temperature, out byte[] values) - && (values is { Length: > 0 }) - ? $"{true} ({string.Join(" ", values)})" - : false.ToString(); - return $"Luminance: {vcpCodeValues.ContainsKey((byte)VcpCode.Luminance)}, " + $"Contrast: {vcpCodeValues.ContainsKey((byte)VcpCode.Contrast)}, " + - $"Color Temperature: {temperatureString}, " + + $"Temperature: {vcpCodeValues.ContainsKey((byte)VcpCode.Temperature)}, " + + $"Input Source: {vcpCodeValues.ContainsKey((byte)VcpCode.InputSource)}, " + $"Speaker Volume: {vcpCodeValues.ContainsKey((byte)VcpCode.SpeakerVolume)}, " + $"Power Mode: {vcpCodeValues.ContainsKey((byte)VcpCode.PowerMode)}"; } @@ -372,7 +381,7 @@ 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) + private static Dictionary ExtractVcpCodes(string source) { var dic = new Dictionary(); @@ -456,6 +465,26 @@ private static Dictionary GetVcpCodeValues(string source) static bool IsHexNumber(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f'); } + private static Dictionary FilterVcpCodes(Dictionary dic) + { + if (dic.TryGetValue((byte)VcpCode.Temperature, out byte[] values)) + { + // The following color 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. + dic[(byte)VcpCode.Temperature] = values.Clip(3, 10).ToArray(); // 3 is warmest and 10 is coldest. + } + return dic; + } + /// /// Gets raw brightnesses not represented in percentage. /// @@ -512,18 +541,12 @@ 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) + public static (AccessResult result, uint minimum, uint current, uint maximum) GetValue(SafePhysicalMonitorHandle physicalMonitorHandle, byte code) { - var (result, _, current, _) = GetVcpValue(physicalMonitorHandle, VcpCode.Temperature); - return (result, (byte)current); + if (!TryConvertVcpCode(code, out VcpCode vcpCode)) + return (result: AccessResult.NotSupported, 0, 0, 0); + + return GetVcpValue(physicalMonitorHandle, vcpCode); } private static (AccessResult result, uint minimum, uint current, uint maximum) GetVcpValue(SafePhysicalMonitorHandle physicalMonitorHandle, VcpCode vcpCode) @@ -594,15 +617,12 @@ 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) + public static AccessResult SetValue(SafePhysicalMonitorHandle physicalMonitorHandle, byte code, uint value) { - return SetVcpValue(physicalMonitorHandle, VcpCode.Temperature, temperature); + if (!TryConvertVcpCode(code, out VcpCode vcpCode)) + return AccessResult.NotSupported; + + return SetVcpValue(physicalMonitorHandle, vcpCode, value); } private static AccessResult SetVcpValue(SafePhysicalMonitorHandle physicalMonitorHandle, VcpCode vcpCode, uint value) @@ -695,50 +715,22 @@ internal class MonitorCapability [DataMember(Order = 3)] public bool IsPrecleared { 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 IReadOnlyDictionary> CapabilitiesCodes { get; } - public bool IsTemperatureSupported => (Temperatures is { Count: > 0 }); - [DataMember(Order = 4, Name = nameof(IsTemperatureSupported))] - private string _isTemperatureSupportedString; - - [DataMember(Order = 5)] + [DataMember(Order = 4)] public string CapabilitiesString { get; } - [DataMember(Order = 6)] + [DataMember(Order = 5)] public string CapabilitiesReport { get; } - [DataMember(Order = 7)] + [DataMember(Order = 6)] 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, + IReadOnlyDictionary capabilitiesCodes = null, string capabilitiesString = null, string capabilitiesReport = null, byte[] capabilitiesData = null) : this( @@ -746,7 +738,7 @@ public MonitorCapability( isLowLevelBrightnessSupported: isLowLevelBrightnessSupported, isContrastSupported: isContrastSupported, isPrecleared: false, - temperatures: temperatures, + capabilitiesCodes: capabilitiesCodes, capabilitiesString: capabilitiesString, capabilitiesReport: capabilitiesReport, capabilitiesData: capabilitiesData) @@ -757,7 +749,7 @@ private MonitorCapability( bool isLowLevelBrightnessSupported, bool isContrastSupported, bool isPrecleared, - IReadOnlyList temperatures, + IReadOnlyDictionary capabilitiesCodes, string capabilitiesString, string capabilitiesReport, byte[] capabilitiesData) @@ -766,7 +758,7 @@ private MonitorCapability( this.IsLowLevelBrightnessSupported = isLowLevelBrightnessSupported; this.IsContrastSupported = isContrastSupported; this.IsPrecleared = isPrecleared; - this.Temperatures = temperatures?.Clip(3, 10).ToArray(); // 3 is warmest and 10 is coldest. + this.CapabilitiesCodes = (capabilitiesCodes is not null) ? new ReadOnlyDictionary>(capabilitiesCodes.ToDictionary(x => x.Key, x => (x.Value is not null) ? (IReadOnlyList)Array.AsReadOnly(x.Value) : null)) : null; this.CapabilitiesString = capabilitiesString; this.CapabilitiesReport = capabilitiesReport; this.CapabilitiesData = (capabilitiesData is not null) ? Convert.ToBase64String(capabilitiesData) : null; @@ -778,7 +770,7 @@ private MonitorCapability( isLowLevelBrightnessSupported: true, isContrastSupported: true, isPrecleared: true, - temperatures: null, + capabilitiesCodes: 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 1d880322..8158a4d7 100644 --- a/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs +++ b/Source/Monitorian.Core/Models/Monitor/MonitorItem.cs @@ -22,7 +22,6 @@ 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, @@ -58,7 +57,7 @@ public MonitorItem( public virtual AccessResult UpdateContrast() => AccessResult.NotSupported; public virtual AccessResult SetContrast(int contrast) => AccessResult.NotSupported; - public virtual AccessResult ChangeTemperature() => AccessResult.NotSupported; + public virtual AccessResult ChangeValue(byte code, int value = -1) => AccessResult.NotSupported; public override string ToString() { @@ -74,7 +73,6 @@ 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 a8365359..71daf21f 100644 --- a/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs +++ b/Source/Monitorian.Core/ViewModels/MonitorViewModel.cs @@ -392,14 +392,35 @@ private bool SetContrast(int contrast) #endregion - #region Temperature + #region Non-Continuous - public bool IsTemperatureSupported => _monitor.IsTemperatureSupported; - - public void ChangeTemperature() + public void ChangeValue(byte code, int value = -1) { - if (IsTemperatureSupported) - _monitor.ChangeTemperature(); + AccessResult result; + lock (_lock) + { + result = _monitor.ChangeValue(code, value); + } + + switch (result.Status) + { + case AccessStatus.NotSupported: + case AccessStatus.Succeeded: + break; + + default: + _controller.OnMonitorAccessFailed(result); + + switch (result.Status) + { + case AccessStatus.DdcFailed: + case AccessStatus.TransmissionFailed: + case AccessStatus.NoLongerExist: + _controller.OnMonitorsChangeFound(); + break; + } + break; + } } #endregion From d23eab1e403184a03b6d0fd27b90fb74e67dd25a Mon Sep 17 00:00:00 2001 From: emoacht Date: Mon, 15 May 2023 12:17:47 +0900 Subject: [PATCH 3/3] Increment build number --- Source/Installer/Product.wxs | 2 +- Source/Monitorian.Core/Properties/AssemblyInfo.cs | 4 ++-- Source/Monitorian/Properties/AssemblyInfo.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Installer/Product.wxs b/Source/Installer/Product.wxs index 0bef8cce..47dabef4 100644 --- a/Source/Installer/Product.wxs +++ b/Source/Installer/Product.wxs @@ -1,6 +1,6 @@  -