Refactor: unify VCP feature handling with VcpFeatureValue

Replaced BrightnessInfo with generic VcpFeatureValue struct to represent any VCP feature (brightness, color temp, input source, etc.). Updated IMonitorController and all controller implementations to use VcpFeatureValue for relevant methods. Simplified and unified VCP set/get logic, removed redundant validation and obsolete DdcCiNative methods, and updated helper classes accordingly. Improved code clarity and maintainability by generalizing VCP feature handling throughout the codebase.
This commit is contained in:
Yu Leng
2025-12-10 19:04:19 +08:00
parent ecd8331d51
commit 6aa7e2cdf6
8 changed files with 64 additions and 375 deletions

View File

@@ -60,7 +60,7 @@ namespace PowerDisplay.Common.Drivers.DDC
/// <summary>
/// Get monitor brightness using VCP code 0x10
/// </summary>
public async Task<BrightnessInfo> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
return await GetVcpFeatureAsync(monitor, VcpCodeBrightness, "Brightness", cancellationToken);
@@ -70,25 +70,25 @@ namespace PowerDisplay.Common.Drivers.DDC
/// Set monitor brightness using VCP code 0x10
/// </summary>
public Task<MonitorOperationResult> SetBrightnessAsync(Monitor monitor, int brightness, CancellationToken cancellationToken = default)
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeBrightness, brightness, 0, 100, cancellationToken);
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeBrightness, brightness, cancellationToken);
/// <summary>
/// Set monitor contrast
/// </summary>
public Task<MonitorOperationResult> SetContrastAsync(Monitor monitor, int contrast, CancellationToken cancellationToken = default)
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeContrast, contrast, 0, 100, cancellationToken);
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeContrast, contrast, cancellationToken);
/// <summary>
/// Set monitor volume
/// </summary>
public Task<MonitorOperationResult> SetVolumeAsync(Monitor monitor, int volume, CancellationToken cancellationToken = default)
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeVolume, volume, 0, 100, cancellationToken);
=> SetVcpFeatureAsync(monitor, NativeConstants.VcpCodeVolume, volume, cancellationToken);
/// <summary>
/// Get monitor color temperature using VCP code 0x14 (Select Color Preset)
/// Returns the raw VCP preset value (e.g., 0x05 for 6500K), not Kelvin temperature
/// </summary>
public async Task<BrightnessInfo> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
return await GetVcpFeatureAsync(monitor, VcpCodeSelectColorPreset, "Color temperature", cancellationToken);
@@ -97,56 +97,14 @@ namespace PowerDisplay.Common.Drivers.DDC
/// <summary>
/// Set monitor color temperature using VCP code 0x14 (Select Color Preset)
/// </summary>
/// <param name="monitor">Monitor to control</param>
/// <param name="colorTemperature">VCP preset value (e.g., 0x05 for 6500K), not Kelvin temperature</param>
/// <param name="cancellationToken">Cancellation token</param>
public async Task<MonitorOperationResult> SetColorTemperatureAsync(Monitor monitor, int colorTemperature, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
return await Task.Run(
() =>
{
if (monitor.Handle == IntPtr.Zero)
{
return MonitorOperationResult.Failure("Invalid monitor handle");
}
try
{
// Validate value is in supported list
var validationError = ValidateDiscreteVcpValue(monitor, VcpCodeSelectColorPreset, colorTemperature, "Color preset");
if (validationError != null)
{
return validationError.Value;
}
// Set VCP 0x14 value
var presetName = VcpValueNames.GetFormattedName(VcpCodeSelectColorPreset, colorTemperature);
if (DdcCiNative.TrySetVCPFeature(monitor.Handle, VcpCodeSelectColorPreset, (uint)colorTemperature))
{
Logger.LogInfo($"[{monitor.Id}] Set color temperature to {presetName} via 0x14");
return MonitorOperationResult.Success();
}
var lastError = GetLastError();
Logger.LogError($"[{monitor.Id}] Failed to set color temperature, error: {lastError}");
return MonitorOperationResult.Failure("Failed to set color temperature via DDC/CI", (int)lastError);
}
catch (Exception ex)
{
Logger.LogError($"[{monitor.Id}] Exception setting color temperature: {ex.Message}");
return MonitorOperationResult.Failure($"Exception setting color temperature: {ex.Message}");
}
},
cancellationToken);
}
public Task<MonitorOperationResult> SetColorTemperatureAsync(Monitor monitor, int colorTemperature, CancellationToken cancellationToken = default)
=> SetVcpFeatureAsync(monitor, VcpCodeSelectColorPreset, colorTemperature, cancellationToken);
/// <summary>
/// Get current input source using VCP code 0x60
/// Returns the raw VCP value (e.g., 0x11 for HDMI-1)
/// </summary>
public async Task<BrightnessInfo> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
return await GetVcpFeatureAsync(monitor, VcpCodeInputSource, "Input source", cancellationToken);
@@ -155,83 +113,8 @@ namespace PowerDisplay.Common.Drivers.DDC
/// <summary>
/// Set input source using VCP code 0x60
/// </summary>
/// <param name="monitor">Monitor to control</param>
/// <param name="inputSource">VCP input source value (e.g., 0x11 for HDMI-1)</param>
/// <param name="cancellationToken">Cancellation token</param>
public async Task<MonitorOperationResult> SetInputSourceAsync(Monitor monitor, int inputSource, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
return await Task.Run(
async () =>
{
if (monitor.Handle == IntPtr.Zero)
{
return MonitorOperationResult.Failure("Invalid monitor handle");
}
try
{
// Validate value is in supported list
var validationError = ValidateDiscreteVcpValue(monitor, VcpCodeInputSource, inputSource, "Input source");
if (validationError != null)
{
return validationError.Value;
}
// Set VCP 0x60 value
var sourceName = VcpValueNames.GetFormattedName(VcpCodeInputSource, inputSource);
if (DdcCiNative.TrySetVCPFeature(monitor.Handle, VcpCodeInputSource, (uint)inputSource))
{
Logger.LogInfo($"[{monitor.Id}] Set input source to {sourceName} via 0x60");
// Verify the change by reading back the value after a short delay
await VerifyInputSourceChangeAsync(monitor, inputSource, cancellationToken);
// Update the monitor model with the new value
monitor.CurrentInputSource = inputSource;
return MonitorOperationResult.Success();
}
var lastError = GetLastError();
Logger.LogError($"[{monitor.Id}] Failed to set input source, error: {lastError}");
return MonitorOperationResult.Failure("Failed to set input source via DDC/CI", (int)lastError);
}
catch (Exception ex)
{
Logger.LogError($"[{monitor.Id}] Exception setting input source: {ex.Message}");
return MonitorOperationResult.Failure($"Exception setting input source: {ex.Message}");
}
},
cancellationToken);
}
/// <summary>
/// Verify input source change by reading back the value after a short delay.
/// Logs warning if verification fails or value doesn't match.
/// </summary>
private static async Task VerifyInputSourceChangeAsync(Monitor monitor, int expectedValue, CancellationToken cancellationToken)
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
if (DdcCiNative.TryGetVCPFeature(monitor.Handle, VcpCodeInputSource, out uint verifyValue, out uint _))
{
var verifyName = VcpValueNames.GetFormattedName(VcpCodeInputSource, (int)verifyValue);
if (verifyValue == (uint)expectedValue)
{
Logger.LogDebug($"[{monitor.Id}] Input source verified: {verifyName} (0x{verifyValue:X2})");
}
else
{
Logger.LogWarning($"[{monitor.Id}] Input source verification mismatch! Expected 0x{expectedValue:X2}, got {verifyName} (0x{verifyValue:X2}). Monitor may have refused to switch (no signal on target port?)");
}
}
else
{
Logger.LogWarning($"[{monitor.Id}] Could not verify input source change");
}
}
public Task<MonitorOperationResult> SetInputSourceAsync(Monitor monitor, int inputSource, CancellationToken cancellationToken = default)
=> SetVcpFeatureAsync(monitor, VcpCodeInputSource, inputSource, cancellationToken);
/// <summary>
/// Get monitor capabilities string with retry logic.
@@ -454,18 +337,7 @@ namespace PowerDisplay.Common.Drivers.DDC
var monitorInfo = matchingInfos[i];
// Generate unique monitor Id
var monitorId = !string.IsNullOrEmpty(monitorInfo.HardwareId)
? $"DDC_{monitorInfo.HardwareId}_{monitorInfo.MonitorNumber}"
: $"DDC_Unknown_{monitorInfo.MonitorNumber}";
var (handleToUse, _) = _handleManager.ReuseOrCreateHandle(monitorId, physicalMonitor.HPhysicalMonitor);
var monitorToCreate = physicalMonitor;
monitorToCreate.HPhysicalMonitor = handleToUse;
candidates.Add(new CandidateMonitor(handleToUse, monitorToCreate, monitorInfo));
candidates.Add(new CandidateMonitor(physicalMonitor.HPhysicalMonitor, physicalMonitor, monitorInfo));
Logger.LogDebug($"DDC: Candidate {gdiDeviceName} -> DevicePath={monitorInfo.DevicePath}, HardwareId={monitorInfo.HardwareId}");
}
}
@@ -561,7 +433,7 @@ namespace PowerDisplay.Common.Drivers.DDC
/// </summary>
private static void InitializeInputSource(Monitor monitor, IntPtr handle)
{
if (DdcCiNative.TryGetVCPFeature(handle, VcpCodeInputSource, out uint current, out uint _))
if (GetVCPFeatureAndVCPFeatureReply(handle, VcpCodeInputSource, IntPtr.Zero, out uint current, out uint _))
{
monitor.CurrentInputSource = (int)current;
Logger.LogDebug($"[{monitor.Id}] Input source: {VcpValueNames.GetFormattedName(VcpCodeInputSource, (int)current)}");
@@ -573,7 +445,7 @@ namespace PowerDisplay.Common.Drivers.DDC
/// </summary>
private static void InitializeColorTemperature(Monitor monitor, IntPtr handle)
{
if (DdcCiNative.TryGetVCPFeature(handle, VcpCodeSelectColorPreset, out uint current, out uint _))
if (GetVCPFeatureAndVCPFeatureReply(handle, VcpCodeSelectColorPreset, IntPtr.Zero, out uint current, out uint _))
{
monitor.CurrentColorTemperature = (int)current;
Logger.LogDebug($"[{monitor.Id}] Color temperature: {VcpValueNames.GetFormattedName(VcpCodeSelectColorPreset, (int)current)}");
@@ -668,7 +540,7 @@ namespace PowerDisplay.Common.Drivers.DDC
/// <param name="vcpCode">VCP code to read</param>
/// <param name="featureName">Optional feature name for logging (e.g., "color temperature", "input source")</param>
/// <param name="cancellationToken">Cancellation token</param>
private async Task<BrightnessInfo> GetVcpFeatureAsync(
private async Task<VcpFeatureValue> GetVcpFeatureAsync(
Monitor monitor,
byte vcpCode,
string? featureName = null,
@@ -684,10 +556,10 @@ namespace PowerDisplay.Common.Drivers.DDC
Logger.LogDebug($"[{monitor.Id}] Invalid handle for {featureName} read");
}
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
if (DdcCiNative.TryGetVCPFeature(monitor.Handle, vcpCode, out uint current, out uint max))
if (GetVCPFeatureAndVCPFeatureReply(monitor.Handle, vcpCode, IntPtr.Zero, out uint current, out uint max))
{
if (featureName != null)
{
@@ -695,7 +567,7 @@ namespace PowerDisplay.Common.Drivers.DDC
Logger.LogDebug($"[{monitor.Id}] {featureName} via 0x{vcpCode:X2}: {valueName}");
}
return new BrightnessInfo((int)current, 0, (int)max);
return new VcpFeatureValue((int)current, 0, (int)max);
}
if (featureName != null)
@@ -703,58 +575,24 @@ namespace PowerDisplay.Common.Drivers.DDC
Logger.LogWarning($"[{monitor.Id}] Failed to read {featureName} (0x{vcpCode:X2} not supported)");
}
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
},
cancellationToken);
}
/// <summary>
/// Validate that a discrete VCP value is supported by the monitor.
/// Returns null if valid, or a failure result if invalid.
/// Generic method to set VCP feature value directly.
/// </summary>
/// <param name="monitor">Monitor to validate against</param>
/// <param name="vcpCode">VCP code to check</param>
/// <param name="value">Value to validate</param>
/// <param name="featureName">Feature name for error messages</param>
/// <returns>Null if valid, MonitorOperationResult.Failure if invalid</returns>
private static MonitorOperationResult? ValidateDiscreteVcpValue(
private Task<MonitorOperationResult> SetVcpFeatureAsync(
Monitor monitor,
byte vcpCode,
int value,
string featureName)
{
var capabilities = monitor.VcpCapabilitiesInfo;
if (capabilities == null || !capabilities.SupportsVcpCode(vcpCode))
{
return null; // No capabilities to validate against, allow the operation
}
var supportedValues = capabilities.GetSupportedValues(vcpCode);
if (supportedValues == null || supportedValues.Count == 0 || supportedValues.Contains(value))
{
return null; // Value is valid or no discrete values defined
}
var supportedList = string.Join(", ", supportedValues.Select(v => $"0x{v:X2}"));
Logger.LogWarning($"[{monitor.Id}] {featureName} 0x{value:X2} not in supported list: [{supportedList}]");
return MonitorOperationResult.Failure($"{featureName} 0x{value:X2} not supported by monitor");
}
/// <summary>
/// Generic method to set VCP feature value
/// </summary>
private async Task<MonitorOperationResult> SetVcpFeatureAsync(
Monitor monitor,
byte vcpCode,
int value,
int min = 0,
int max = 100,
CancellationToken cancellationToken = default)
{
value = Math.Clamp(value, min, max);
ArgumentNullException.ThrowIfNull(monitor);
return await Task.Run(
async () =>
return Task.Run(
() =>
{
if (monitor.Handle == IntPtr.Zero)
{
@@ -763,16 +601,7 @@ namespace PowerDisplay.Common.Drivers.DDC
try
{
// Get current value to determine range
var currentInfo = await GetVcpFeatureAsync(monitor, vcpCode);
if (!currentInfo.IsValid)
{
return MonitorOperationResult.Failure($"Cannot read current value for VCP 0x{vcpCode:X2}");
}
uint targetValue = (uint)currentInfo.FromPercentage(value);
if (DdcCiNative.TrySetVCPFeature(monitor.Handle, vcpCode, targetValue))
if (SetVCPFeature(monitor.Handle, vcpCode, (uint)value))
{
return MonitorOperationResult.Success();
}

View File

@@ -82,62 +82,6 @@ namespace PowerDisplay.Common.Drivers.DDC
/// </summary>
public static class DdcCiNative
{
// Helper Methods
/// <summary>
/// Safe wrapper for getting VCP feature value
/// </summary>
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
/// <param name="vcpCode">VCP code</param>
/// <param name="currentValue">Current value</param>
/// <param name="maxValue">Maximum value</param>
/// <returns>True if successful</returns>
public static bool TryGetVCPFeature(IntPtr hPhysicalMonitor, byte vcpCode, out uint currentValue, out uint maxValue)
{
currentValue = 0;
maxValue = 0;
if (hPhysicalMonitor == IntPtr.Zero)
{
return false;
}
try
{
return GetVCPFeatureAndVCPFeatureReply(hPhysicalMonitor, vcpCode, IntPtr.Zero, out currentValue, out maxValue);
}
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogDebug($"TryGetVCPFeature failed for VCP code 0x{vcpCode:X2}: {ex.Message}");
return false;
}
}
/// <summary>
/// Safe wrapper for setting VCP feature value
/// </summary>
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
/// <param name="vcpCode">VCP code</param>
/// <param name="value">New value</param>
/// <returns>True if successful</returns>
public static bool TrySetVCPFeature(IntPtr hPhysicalMonitor, byte vcpCode, uint value)
{
if (hPhysicalMonitor == IntPtr.Zero)
{
return false;
}
try
{
return SetVCPFeature(hPhysicalMonitor, vcpCode, value);
}
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogDebug($"TrySetVCPFeature failed for VCP code 0x{vcpCode:X2}: {ex.Message}");
return false;
}
}
/// <summary>
/// Fetches VCP capabilities string from a monitor and returns a validation result.
/// This is the slow I2C operation (~4 seconds per monitor) that should only be done once.
@@ -184,46 +128,6 @@ namespace PowerDisplay.Common.Drivers.DDC
}
}
/// <summary>
/// Validates the DDC/CI connection by checking if the monitor returns a valid capabilities string
/// that includes brightness control (VCP 0x10).
/// NOTE: This method performs a slow I2C operation. Prefer using FetchCapabilities() during
/// discovery phase and caching the result.
/// </summary>
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
/// <returns>Validation result containing status and cached capabilities data</returns>
[System.Obsolete("Use FetchCapabilities() during discovery and cache results. This method is kept for backward compatibility.")]
public static DdcCiValidationResult ValidateDdcCiConnection(IntPtr hPhysicalMonitor)
{
// Delegate to FetchCapabilities which does the same thing
return FetchCapabilities(hPhysicalMonitor);
}
/// <summary>
/// Quick connection check using a simple VCP read (brightness).
/// This is much faster than full capabilities retrieval (~50ms vs ~4s).
/// Use this for runtime connection validation when capabilities are already cached.
/// </summary>
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
/// <returns>True if the monitor responds to VCP queries</returns>
public static bool QuickConnectionCheck(IntPtr hPhysicalMonitor)
{
if (hPhysicalMonitor == IntPtr.Zero)
{
return false;
}
try
{
// Try a quick brightness read via VCP 0x10 to verify connection
return TryGetVCPFeature(hPhysicalMonitor, NativeConstants.VcpCodeBrightness, out _, out _);
}
catch
{
return false;
}
}
/// <summary>
/// Try to get capabilities string from a physical monitor handle.
/// </summary>

View File

@@ -189,14 +189,14 @@ namespace PowerDisplay.Common.Drivers.DDC
/// <summary>
/// Get current brightness using VCP code 0x10
/// </summary>
private BrightnessInfo GetCurrentBrightness(IntPtr handle)
private VcpFeatureValue GetCurrentBrightness(IntPtr handle)
{
if (DdcCiNative.TryGetVCPFeature(handle, VcpCodeBrightness, out uint current, out uint max))
if (GetVCPFeatureAndVCPFeatureReply(handle, VcpCodeBrightness, IntPtr.Zero, out uint current, out uint max))
{
return new BrightnessInfo((int)current, 0, (int)max);
return new VcpFeatureValue((int)current, 0, (int)max);
}
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
/// <summary>

View File

@@ -41,38 +41,6 @@ namespace PowerDisplay.Common.Drivers.DDC
return IntPtr.Zero;
}
/// <summary>
/// Try to reuse existing handle if valid; otherwise uses new handle
/// Returns the handle to use and whether it was reused
/// </summary>
public (IntPtr Handle, bool WasReused) ReuseOrCreateHandle(string monitorId, IntPtr newHandle)
{
if (string.IsNullOrEmpty(monitorId))
{
return (newHandle, false);
}
return _monitorIdToHandleMap.ExecuteWithLock(dict =>
{
// Try to reuse existing handle if it's still valid
// Use quick connection check instead of full capabilities retrieval
if (dict.TryGetValue(monitorId, out var existingHandle) &&
existingHandle != IntPtr.Zero &&
DdcCiNative.QuickConnectionCheck(existingHandle))
{
// Destroy the newly created handle since we're using the old one
if (newHandle != existingHandle && newHandle != IntPtr.Zero)
{
DestroyPhysicalMonitor(newHandle);
}
return (existingHandle, true);
}
return (newHandle, false);
});
}
/// <summary>
/// Update the handle mapping with new handles
/// </summary>

View File

@@ -139,7 +139,7 @@ namespace PowerDisplay.Common.Drivers.WMI
/// <summary>
/// Get monitor brightness
/// </summary>
public async Task<BrightnessInfo> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(monitor);
@@ -158,7 +158,7 @@ namespace PowerDisplay.Common.Drivers.WMI
foreach (var obj in results)
{
var currentBrightness = obj.GetPropertyValue<byte>("CurrentBrightness");
return new BrightnessInfo(currentBrightness, 0, 100);
return new VcpFeatureValue(currentBrightness, 0, 100);
}
// No match found - monitor may have been disconnected
@@ -173,7 +173,7 @@ namespace PowerDisplay.Common.Drivers.WMI
Logger.LogWarning($"WMI GetBrightness failed: {ex.Message}");
}
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
},
cancellationToken);
}
@@ -447,9 +447,9 @@ namespace PowerDisplay.Common.Drivers.WMI
return Task.FromResult(MonitorOperationResult.Failure("Volume control not supported via WMI"));
}
public Task<BrightnessInfo> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default)
public Task<VcpFeatureValue> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
return Task.FromResult(BrightnessInfo.Invalid);
return Task.FromResult(VcpFeatureValue.Invalid);
}
public Task<MonitorOperationResult> SetColorTemperatureAsync(Monitor monitor, int colorTemperature, CancellationToken cancellationToken = default)
@@ -457,10 +457,10 @@ namespace PowerDisplay.Common.Drivers.WMI
return Task.FromResult(MonitorOperationResult.Failure("Color temperature control not supported via WMI"));
}
public Task<BrightnessInfo> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default)
public Task<VcpFeatureValue> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
// Input source switching not supported for internal displays
return Task.FromResult(BrightnessInfo.Invalid);
return Task.FromResult(VcpFeatureValue.Invalid);
}
public Task<MonitorOperationResult> SetInputSourceAsync(Monitor monitor, int inputSource, CancellationToken cancellationToken = default)
@@ -469,11 +469,6 @@ namespace PowerDisplay.Common.Drivers.WMI
return Task.FromResult(MonitorOperationResult.Failure("Input source switching not supported via WMI"));
}
public Task<string> GetCapabilitiesStringAsync(Monitor monitor, CancellationToken cancellationToken = default)
{
return Task.FromResult(string.Empty);
}
public void Dispose()
{
Dispose(true);

View File

@@ -27,7 +27,7 @@ namespace PowerDisplay.Common.Interfaces
/// <param name="monitor">Monitor object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Brightness information</returns>
Task<BrightnessInfo> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default);
Task<VcpFeatureValue> GetBrightnessAsync(Monitor monitor, CancellationToken cancellationToken = default);
/// <summary>
/// Sets monitor brightness
@@ -69,7 +69,7 @@ namespace PowerDisplay.Common.Interfaces
/// <param name="monitor">Monitor object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>VCP preset value (e.g., 0x05 for 6500K), not Kelvin temperature</returns>
Task<BrightnessInfo> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default);
Task<VcpFeatureValue> GetColorTemperatureAsync(Monitor monitor, CancellationToken cancellationToken = default);
/// <summary>
/// Sets monitor color temperature using VCP 0x14 preset value
@@ -86,7 +86,7 @@ namespace PowerDisplay.Common.Interfaces
/// <param name="monitor">Monitor object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>VCP input source value (e.g., 0x11 for HDMI-1)</returns>
Task<BrightnessInfo> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default);
Task<VcpFeatureValue> GetInputSourceAsync(Monitor monitor, CancellationToken cancellationToken = default);
/// <summary>
/// Sets input source using VCP 0x60
@@ -97,14 +97,6 @@ namespace PowerDisplay.Common.Interfaces
/// <returns>Operation result</returns>
Task<MonitorOperationResult> SetInputSourceAsync(Monitor monitor, int inputSource, CancellationToken cancellationToken = default);
/// <summary>
/// Gets monitor capabilities string (DDC/CI)
/// </summary>
/// <param name="monitor">Monitor object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Capabilities string</returns>
Task<string> GetCapabilitiesStringAsync(Monitor monitor, CancellationToken cancellationToken = default);
/// <summary>
/// Releases resources
/// </summary>

View File

@@ -7,36 +7,37 @@ using System;
namespace PowerDisplay.Common.Models
{
/// <summary>
/// Brightness information structure
/// VCP feature value information structure.
/// Represents the current, minimum, and maximum values for a VCP (Virtual Control Panel) feature.
/// </summary>
public readonly struct BrightnessInfo
public readonly struct VcpFeatureValue
{
/// <summary>
/// Current brightness value
/// Current value
/// </summary>
public int Current { get; }
/// <summary>
/// Minimum brightness value
/// Minimum value
/// </summary>
public int Minimum { get; }
/// <summary>
/// Maximum brightness value
/// Maximum value
/// </summary>
public int Maximum { get; }
/// <summary>
/// Whether the brightness information is valid
/// Whether the value information is valid
/// </summary>
public bool IsValid { get; }
/// <summary>
/// Timestamp when the brightness information was obtained
/// Timestamp when the value information was obtained
/// </summary>
public DateTime Timestamp { get; }
public BrightnessInfo(int current, int minimum, int maximum)
public VcpFeatureValue(int current, int minimum, int maximum)
{
Current = current;
Minimum = minimum;
@@ -45,18 +46,18 @@ namespace PowerDisplay.Common.Models
Timestamp = DateTime.Now;
}
public BrightnessInfo(int current, int maximum)
public VcpFeatureValue(int current, int maximum)
: this(current, 0, maximum)
{
}
/// <summary>
/// Creates invalid brightness information
/// Creates invalid value information
/// </summary>
public static BrightnessInfo Invalid => new(-1, -1, -1);
public static VcpFeatureValue Invalid => new(-1, -1, -1);
/// <summary>
/// Converts brightness value to percentage (0-100)
/// Converts value to percentage (0-100)
/// </summary>
public int ToPercentage()
{
@@ -69,7 +70,7 @@ namespace PowerDisplay.Common.Models
}
/// <summary>
/// Creates brightness value from percentage
/// Creates raw value from percentage
/// </summary>
public int FromPercentage(int percentage)
{

View File

@@ -153,18 +153,18 @@ namespace PowerDisplay.Helpers
/// <summary>
/// Get brightness of the specified monitor
/// </summary>
public async Task<BrightnessInfo> GetBrightnessAsync(string monitorId, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetBrightnessAsync(string monitorId, CancellationToken cancellationToken = default)
{
var monitor = GetMonitor(monitorId);
if (monitor == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
var controller = GetControllerForMonitor(monitor);
if (controller == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
try
@@ -184,7 +184,7 @@ namespace PowerDisplay.Helpers
// Mark monitor as unavailable
Logger.LogError($"Failed to get brightness for monitor {monitorId}: {ex.Message}");
monitor.IsAvailable = false;
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
}
@@ -224,18 +224,18 @@ namespace PowerDisplay.Helpers
/// <summary>
/// Get monitor color temperature
/// </summary>
public async Task<BrightnessInfo> GetColorTemperatureAsync(string monitorId, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetColorTemperatureAsync(string monitorId, CancellationToken cancellationToken = default)
{
var monitor = GetMonitor(monitorId);
if (monitor == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
var controller = GetControllerForMonitor(monitor);
if (controller == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
try
@@ -245,7 +245,7 @@ namespace PowerDisplay.Helpers
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogDebug($"GetColorTemperatureAsync failed: {ex.Message}");
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
}
@@ -263,18 +263,18 @@ namespace PowerDisplay.Helpers
/// <summary>
/// Get current input source for a monitor
/// </summary>
public async Task<BrightnessInfo> GetInputSourceAsync(string monitorId, CancellationToken cancellationToken = default)
public async Task<VcpFeatureValue> GetInputSourceAsync(string monitorId, CancellationToken cancellationToken = default)
{
var monitor = GetMonitor(monitorId);
if (monitor == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
var controller = GetControllerForMonitor(monitor);
if (controller == null)
{
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
try
@@ -284,7 +284,7 @@ namespace PowerDisplay.Helpers
catch (Exception ex) when (ex is not OutOfMemoryException)
{
Logger.LogDebug($"GetInputSourceAsync failed: {ex.Message}");
return BrightnessInfo.Invalid;
return VcpFeatureValue.Invalid;
}
}