Skip to content

Commit

Permalink
Merge pull request #470 from emoacht/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
emoacht authored May 15, 2023
2 parents 29d87ca + d23eab1 commit 71d4e2b
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Source/Installer/Product.wxs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Monitorian" Manufacturer="emoacht" Version="4.3.3"
<Product Id="*" Name="Monitorian" Manufacturer="emoacht" Version="4.3.4"
Language="1033" Codepage="1252" UpgradeCode="{81A4D148-75D3-462E-938D-8C208FB48E3C}">
<Package Id="*" InstallerVersion="500" Compressed="yes"
InstallScope="perMachine" InstallPrivileges="elevated"
Expand Down
13 changes: 8 additions & 5 deletions Source/Monitorian.Core/AppControllerCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
16 changes: 10 additions & 6 deletions Source/Monitorian.Core/Models/Monitor/DdcMonitorItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down
3 changes: 1 addition & 2 deletions Source/Monitorian.Core/Models/Monitor/IMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -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
Expand Down
132 changes: 62 additions & 70 deletions Source/Monitorian.Core/Models/Monitor/MonitorConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
Expand Down Expand Up @@ -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<PhysicalItem> EnumeratePhysicalMonitors(IntPtr monitorHandle, bool verbose = false)
{
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(
Expand Down Expand Up @@ -264,16 +277,16 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle
capabilitiesStringLength))
{
var capabilitiesString = buffer.ToString();
IReadOnlyDictionary<byte, byte[]> vcpCodeValues = GetVcpCodeValues(capabilitiesString);
Dictionary<byte, byte[]> 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(
Expand All @@ -283,14 +296,10 @@ private static MonitorCapability GetMonitorCapability(SafePhysicalMonitorHandle

static string MakeCapabilitiesReport(IReadOnlyDictionary<byte, byte[]> 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)}";
}
Expand Down Expand Up @@ -372,7 +381,7 @@ private static IEnumerable<byte> EnumerateVcpCodes(string source)
static bool IsHexNumber(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f');
}

private static Dictionary<byte, byte[]> GetVcpCodeValues(string source)
private static Dictionary<byte, byte[]> ExtractVcpCodes(string source)
{
var dic = new Dictionary<byte, byte[]>();

Expand Down Expand Up @@ -456,6 +465,26 @@ private static Dictionary<byte, byte[]> GetVcpCodeValues(string source)
static bool IsHexNumber(char c) => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f');
}

private static Dictionary<byte, byte[]> FilterVcpCodes(Dictionary<byte, byte[]> 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<byte>(3, 10).ToArray(); // 3 is warmest and 10 is coldest.
}
return dic;
}

/// <summary>
/// Gets raw brightnesses not represented in percentage.
/// </summary>
Expand Down Expand Up @@ -512,18 +541,12 @@ public static (AccessResult result, uint minimum, uint current, uint maximum) Ge
return GetVcpValue(physicalMonitorHandle, VcpCode.Contrast);
}

/// <summary>
/// Gets raw color temperature.
/// </summary>
/// <param name="physicalMonitorHandle">Physical monitor handle</param>
/// <returns>
/// <para>result: Result</para>
/// <para>current: Raw current color temperature</para>
/// </returns>
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)
Expand Down Expand Up @@ -594,15 +617,12 @@ public static AccessResult SetContrast(SafePhysicalMonitorHandle physicalMonitor
return SetVcpValue(physicalMonitorHandle, VcpCode.Contrast, contrast);
}

/// <summary>
/// Sets raw color temperature.
/// </summary>
/// <param name="physicalMonitorHandle">Physical monitor handle</param>
/// <param name="temperature">Raw color temperature</param>
/// <returns></returns>
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)
Expand Down Expand Up @@ -695,58 +715,30 @@ internal class MonitorCapability
[DataMember(Order = 3)]
public bool IsPrecleared { get; }

/// <summary>
/// Supported color temperatures
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public IReadOnlyList<byte> Temperatures { get; }
public IReadOnlyDictionary<byte, IReadOnlyList<byte>> 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<byte> temperatures = null,
IReadOnlyDictionary<byte, byte[]> capabilitiesCodes = null,
string capabilitiesString = null,
string capabilitiesReport = null,
byte[] capabilitiesData = null) : this(
isHighLevelBrightnessSupported: isHighLevelBrightnessSupported,
isLowLevelBrightnessSupported: isLowLevelBrightnessSupported,
isContrastSupported: isContrastSupported,
isPrecleared: false,
temperatures: temperatures,
capabilitiesCodes: capabilitiesCodes,
capabilitiesString: capabilitiesString,
capabilitiesReport: capabilitiesReport,
capabilitiesData: capabilitiesData)
Expand All @@ -757,7 +749,7 @@ private MonitorCapability(
bool isLowLevelBrightnessSupported,
bool isContrastSupported,
bool isPrecleared,
IReadOnlyList<byte> temperatures,
IReadOnlyDictionary<byte, byte[]> capabilitiesCodes,
string capabilitiesString,
string capabilitiesReport,
byte[] capabilitiesData)
Expand All @@ -766,7 +758,7 @@ private MonitorCapability(
this.IsLowLevelBrightnessSupported = isLowLevelBrightnessSupported;
this.IsContrastSupported = isContrastSupported;
this.IsPrecleared = isPrecleared;
this.Temperatures = temperatures?.Clip<byte>(3, 10).ToArray(); // 3 is warmest and 10 is coldest.
this.CapabilitiesCodes = (capabilitiesCodes is not null) ? new ReadOnlyDictionary<byte, IReadOnlyList<byte>>(capabilitiesCodes.ToDictionary(x => x.Key, x => (x.Value is not null) ? (IReadOnlyList<byte>)Array.AsReadOnly(x.Value) : null)) : null;
this.CapabilitiesString = capabilitiesString;
this.CapabilitiesReport = capabilitiesReport;
this.CapabilitiesData = (capabilitiesData is not null) ? Convert.ToBase64String(capabilitiesData) : null;
Expand All @@ -778,7 +770,7 @@ private MonitorCapability(
isLowLevelBrightnessSupported: true,
isContrastSupported: true,
isPrecleared: true,
temperatures: null,
capabilitiesCodes: null,
capabilitiesString: null,
capabilitiesReport: null,
capabilitiesData: null));
Expand Down
4 changes: 1 addition & 3 deletions Source/Monitorian.Core/Models/Monitor/MonitorItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()
{
Expand All @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions Source/Monitorian.Core/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.3.3.0")]
[assembly: AssemblyFileVersion("4.3.3.0")]
[assembly: AssemblyVersion("4.3.4.0")]
[assembly: AssemblyFileVersion("4.3.4.0")]
[assembly: NeutralResourcesLanguage("en-US")]

// For unit test
Expand Down
Loading

0 comments on commit 71d4e2b

Please sign in to comment.