mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
Optimize monitor discovery and validation process
Refactored monitor discovery to a two-phase process, enabling parallel capability fetching and reducing total discovery time. Introduced caching of monitor capabilities to avoid redundant I2C operations, improving performance during initialization and runtime validation. Added `DdcCiValidationResult` to encapsulate validation status and cached capabilities. Replaced `ValidateDdcCiConnection` with `FetchCapabilities` for capability retrieval, marking the former as obsolete. Introduced `QuickConnectionCheck` for fast runtime validation. Updated `CanControlMonitorAsync`, `GetCapabilitiesStringAsync`, and `InitializeMonitorCapabilitiesAsync` to leverage cached data. Improved logging for better insights into discovery and validation processes.
This commit is contained in:
@@ -47,7 +47,9 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
public string Name => "DDC/CI Monitor Controller";
|
public string Name => "DDC/CI Monitor Controller";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the specified monitor can be controlled
|
/// Check if the specified monitor can be controlled.
|
||||||
|
/// Uses quick connection check if capabilities are already cached,
|
||||||
|
/// otherwise falls back to full validation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> CanControlMonitorAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
public async Task<bool> CanControlMonitorAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -55,7 +57,23 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
var physicalHandle = GetPhysicalHandle(monitor);
|
var physicalHandle = GetPhysicalHandle(monitor);
|
||||||
return physicalHandle != IntPtr.Zero && DdcCiNative.ValidateDdcCiConnection(physicalHandle);
|
if (physicalHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If monitor already has cached capabilities with brightness support,
|
||||||
|
// use quick connection check instead of full capabilities retrieval
|
||||||
|
if (monitor.VcpCapabilitiesInfo != null &&
|
||||||
|
monitor.VcpCapabilitiesInfo.SupportsVcpCode(NativeConstants.VcpCodeBrightness))
|
||||||
|
{
|
||||||
|
return DdcCiNative.QuickConnectionCheck(physicalHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to full validation for monitors without cached capabilities
|
||||||
|
#pragma warning disable CS0618 // Suppress obsolete warning - needed for backward compatibility
|
||||||
|
return DdcCiNative.ValidateDdcCiConnection(physicalHandle).IsValid;
|
||||||
|
#pragma warning restore CS0618
|
||||||
},
|
},
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -345,10 +363,18 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get monitor capabilities string with retry logic
|
/// Get monitor capabilities string with retry logic.
|
||||||
|
/// Uses cached CapabilitiesRaw if available to avoid slow I2C operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<string> GetCapabilitiesStringAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
public async Task<string> GetCapabilitiesStringAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
// Check if capabilities are already cached
|
||||||
|
if (!string.IsNullOrEmpty(monitor.CapabilitiesRaw))
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"GetCapabilitiesStringAsync: Using cached capabilities for {monitor.Id} (length: {monitor.CapabilitiesRaw.Length})");
|
||||||
|
return monitor.CapabilitiesRaw;
|
||||||
|
}
|
||||||
|
|
||||||
return await Task.Run(
|
return await Task.Run(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
@@ -492,7 +518,9 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
return monitors;
|
return monitors;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get physical handles for each monitor
|
// Phase 1: Collect all candidate monitors with their handles
|
||||||
|
var candidateMonitors = new List<(IntPtr Handle, string DeviceKey, PHYSICAL_MONITOR PhysicalMonitor, string AdapterName, int Index, DisplayDeviceInfo? MatchedDevice)>();
|
||||||
|
|
||||||
foreach (var hMonitor in monitorHandles)
|
foreach (var hMonitor in monitorHandles)
|
||||||
{
|
{
|
||||||
var adapterName = _discoveryHelper.GetMonitorDeviceId(hMonitor);
|
var adapterName = _discoveryHelper.GetMonitorDeviceId(hMonitor);
|
||||||
@@ -511,7 +539,6 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match physical monitors with DisplayDeviceInfo
|
// Match physical monitors with DisplayDeviceInfo
|
||||||
// For each physical monitor on this adapter, find the corresponding DisplayDeviceInfo
|
|
||||||
for (int i = 0; i < physicalMonitors.Length; i++)
|
for (int i = 0; i < physicalMonitors.Length; i++)
|
||||||
{
|
{
|
||||||
var physicalMonitor = physicalMonitors[i];
|
var physicalMonitor = physicalMonitors[i];
|
||||||
@@ -542,29 +569,71 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
string deviceKey = matchedDevice?.DeviceKey ?? $"{adapterName}_{i}";
|
string deviceKey = matchedDevice?.DeviceKey ?? $"{adapterName}_{i}";
|
||||||
|
|
||||||
// Use HandleManager to reuse or create handle
|
// Use HandleManager to reuse or create handle
|
||||||
var (handleToUse, reusingOldHandle) = _handleManager.ReuseOrCreateHandle(deviceKey, physicalMonitor.HPhysicalMonitor);
|
var (handleToUse, _) = _handleManager.ReuseOrCreateHandle(deviceKey, physicalMonitor.HPhysicalMonitor);
|
||||||
|
|
||||||
// Always validate DDC/CI connection, regardless of handle reuse
|
// Update physical monitor handle
|
||||||
// This ensures monitors that don't support DDC/CI (e.g., internal laptop displays)
|
|
||||||
// are not included in the DDC controller's results
|
|
||||||
if (!DdcCiNative.ValidateDdcCiConnection(handleToUse))
|
|
||||||
{
|
|
||||||
Logger.LogDebug($"DDC: Handle 0x{handleToUse:X} (reused={reusingOldHandle}) failed DDC/CI validation, skipping");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update physical monitor handle to use the correct one
|
|
||||||
var monitorToCreate = physicalMonitor;
|
var monitorToCreate = physicalMonitor;
|
||||||
monitorToCreate.HPhysicalMonitor = handleToUse;
|
monitorToCreate.HPhysicalMonitor = handleToUse;
|
||||||
|
|
||||||
var monitor = _discoveryHelper.CreateMonitorFromPhysical(monitorToCreate, adapterName, i, monitorDisplayInfo, matchedDevice);
|
candidateMonitors.Add((handleToUse, deviceKey, monitorToCreate, adapterName, i, matchedDevice));
|
||||||
if (monitor != null)
|
}
|
||||||
{
|
}
|
||||||
monitors.Add(monitor);
|
|
||||||
|
|
||||||
// Store in new map for cleanup
|
// Phase 2: Fetch capabilities in PARALLEL for all candidate monitors
|
||||||
newHandleMap[monitor.DeviceKey] = handleToUse;
|
// This is the slow I2C operation (~4s per monitor), but parallelization
|
||||||
|
// significantly reduces total time when multiple monitors are connected.
|
||||||
|
// Results are cached regardless of success/failure.
|
||||||
|
Logger.LogInfo($"DDC: Phase 2 - Fetching capabilities for {candidateMonitors.Count} monitors in parallel");
|
||||||
|
|
||||||
|
var fetchTasks = candidateMonitors.Select(candidate =>
|
||||||
|
Task.Run(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var capabilitiesResult = DdcCiNative.FetchCapabilities(candidate.Handle);
|
||||||
|
return (Candidate: candidate, CapabilitiesResult: capabilitiesResult);
|
||||||
|
},
|
||||||
|
cancellationToken));
|
||||||
|
|
||||||
|
var fetchResults = await Task.WhenAll(fetchTasks);
|
||||||
|
|
||||||
|
Logger.LogInfo($"DDC: Phase 2 completed - Got results for {fetchResults.Length} monitors");
|
||||||
|
|
||||||
|
// Phase 3: Create monitor objects for valid DDC/CI monitors
|
||||||
|
// A monitor is valid for DDC if it has capabilities with brightness support
|
||||||
|
foreach (var result in fetchResults)
|
||||||
|
{
|
||||||
|
// Skip monitors that don't support DDC/CI brightness control
|
||||||
|
if (!result.CapabilitiesResult.IsValid)
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"DDC: Handle 0x{result.Candidate.Handle:X} - No DDC/CI brightness support, skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitor = _discoveryHelper.CreateMonitorFromPhysical(
|
||||||
|
result.Candidate.PhysicalMonitor,
|
||||||
|
result.Candidate.AdapterName,
|
||||||
|
result.Candidate.Index,
|
||||||
|
monitorDisplayInfo,
|
||||||
|
result.Candidate.MatchedDevice);
|
||||||
|
|
||||||
|
if (monitor != null)
|
||||||
|
{
|
||||||
|
// Attach cached capabilities data - this is the key optimization!
|
||||||
|
// By caching here, we avoid re-fetching during InitializeMonitorCapabilitiesAsync
|
||||||
|
if (!string.IsNullOrEmpty(result.CapabilitiesResult.CapabilitiesString))
|
||||||
|
{
|
||||||
|
monitor.CapabilitiesRaw = result.CapabilitiesResult.CapabilitiesString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.CapabilitiesResult.VcpCapabilitiesInfo != null)
|
||||||
|
{
|
||||||
|
monitor.VcpCapabilitiesInfo = result.CapabilitiesResult.VcpCapabilitiesInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitors.Add(monitor);
|
||||||
|
newHandleMap[monitor.DeviceKey] = result.Candidate.Handle;
|
||||||
|
|
||||||
|
Logger.LogInfo($"DDC: Added monitor {monitor.Id} with {monitor.VcpCapabilitiesInfo?.SupportedVcpCodes.Count ?? 0} VCP codes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,12 +651,13 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validate monitor connection status
|
/// Validate monitor connection status.
|
||||||
|
/// Uses quick VCP read instead of full capabilities retrieval.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> ValidateConnectionAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
public async Task<bool> ValidateConnectionAsync(Monitor monitor, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await Task.Run(
|
return await Task.Run(
|
||||||
() => monitor.Handle != IntPtr.Zero && DdcCiNative.ValidateDdcCiConnection(monitor.Handle),
|
() => monitor.Handle != IntPtr.Zero && DdcCiNative.QuickConnectionCheck(monitor.Handle),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,57 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
public string DeviceKey { get; set; } = string.Empty;
|
public string DeviceKey { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DDC/CI validation result containing both validation status and cached capabilities data.
|
||||||
|
/// This allows reusing capabilities data retrieved during validation, avoiding duplicate I2C calls.
|
||||||
|
/// </summary>
|
||||||
|
public struct DdcCiValidationResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the monitor has a valid DDC/CI connection with brightness support.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsValid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the raw capabilities string retrieved during validation.
|
||||||
|
/// Null if retrieval failed.
|
||||||
|
/// </summary>
|
||||||
|
public string? CapabilitiesString { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parsed VCP capabilities info retrieved during validation.
|
||||||
|
/// Null if parsing failed.
|
||||||
|
/// </summary>
|
||||||
|
public Models.VcpCapabilities? VcpCapabilitiesInfo { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether capabilities retrieval was attempted.
|
||||||
|
/// True means the result is from an actual attempt (success or failure).
|
||||||
|
/// </summary>
|
||||||
|
public bool WasAttempted { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DdcCiValidationResult"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public DdcCiValidationResult(bool isValid, string? capabilitiesString = null, Models.VcpCapabilities? vcpCapabilitiesInfo = null, bool wasAttempted = true)
|
||||||
|
{
|
||||||
|
IsValid = isValid;
|
||||||
|
CapabilitiesString = capabilitiesString;
|
||||||
|
VcpCapabilitiesInfo = vcpCapabilitiesInfo;
|
||||||
|
WasAttempted = wasAttempted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an invalid validation result with no cached data.
|
||||||
|
/// </summary>
|
||||||
|
public static DdcCiValidationResult Invalid => new(false, null, null, true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a result indicating validation was not attempted yet.
|
||||||
|
/// </summary>
|
||||||
|
public static DdcCiValidationResult NotAttempted => new(false, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DDC/CI native API wrapper
|
/// DDC/CI native API wrapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -163,29 +214,131 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates the DDC/CI connection
|
/// 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.
|
||||||
|
/// The result is cached regardless of success or failure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
|
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
|
||||||
/// <returns>True if connection is valid</returns>
|
/// <returns>Validation result with capabilities data (or failure status)</returns>
|
||||||
public static bool ValidateDdcCiConnection(IntPtr hPhysicalMonitor)
|
public static DdcCiValidationResult FetchCapabilities(IntPtr hPhysicalMonitor)
|
||||||
|
{
|
||||||
|
if (hPhysicalMonitor == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return DdcCiValidationResult.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Try to get capabilities string (slow I2C operation)
|
||||||
|
var capsString = TryGetCapabilitiesString(hPhysicalMonitor);
|
||||||
|
if (string.IsNullOrEmpty(capsString))
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"FetchCapabilities: Failed to get capabilities string for handle 0x{hPhysicalMonitor:X}");
|
||||||
|
return DdcCiValidationResult.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the capabilities string
|
||||||
|
var capabilities = Utils.VcpCapabilitiesParser.Parse(capsString);
|
||||||
|
if (capabilities == null || capabilities.SupportedVcpCodes.Count == 0)
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"FetchCapabilities: Failed to parse capabilities string for handle 0x{hPhysicalMonitor:X}");
|
||||||
|
return DdcCiValidationResult.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if brightness (VCP 0x10) is supported - determines DDC/CI validity
|
||||||
|
bool supportsBrightness = capabilities.SupportsVcpCode(NativeConstants.VcpCodeBrightness);
|
||||||
|
|
||||||
|
Logger.LogDebug($"FetchCapabilities: Handle 0x{hPhysicalMonitor:X} - BrightnessSupport={supportsBrightness}, VcpCodes={capabilities.SupportedVcpCodes.Count}");
|
||||||
|
return new DdcCiValidationResult(supportsBrightness, capsString, capabilities);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not OutOfMemoryException)
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"FetchCapabilities: Exception for handle 0x{hPhysicalMonitor:X}: {ex.Message}");
|
||||||
|
return DdcCiValidationResult.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
if (hPhysicalMonitor == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try reading basic VCP codes to validate connection
|
try
|
||||||
var testCodes = new byte[] { NativeConstants.VcpCodeBrightness, NativeConstants.VcpCodeContrast, NativeConstants.VcpCodeVcpVersion, NativeConstants.VcpCodeVolume };
|
|
||||||
|
|
||||||
foreach (var code in testCodes)
|
|
||||||
{
|
{
|
||||||
if (TryGetVCPFeature(hPhysicalMonitor, code, out _, out _))
|
// Try a quick brightness read to verify connection
|
||||||
{
|
return TryGetMonitorBrightness(hPhysicalMonitor, out _, out _, out _);
|
||||||
return true;
|
}
|
||||||
}
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get capabilities string from a physical monitor handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hPhysicalMonitor">Physical monitor handle</param>
|
||||||
|
/// <returns>Capabilities string, or null if failed</returns>
|
||||||
|
private static string? TryGetCapabilitiesString(IntPtr hPhysicalMonitor)
|
||||||
|
{
|
||||||
|
if (hPhysicalMonitor == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
try
|
||||||
|
{
|
||||||
|
// Get capabilities string length
|
||||||
|
if (!GetCapabilitiesStringLength(hPhysicalMonitor, out uint length) || length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffer and get capabilities string
|
||||||
|
var buffer = Marshal.AllocHGlobal((int)length);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!CapabilitiesRequestAndCapabilitiesReply(hPhysicalMonitor, buffer, length))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Marshal.PtrToStringAnsi(buffer);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not OutOfMemoryException)
|
||||||
|
{
|
||||||
|
Logger.LogDebug($"TryGetCapabilitiesString failed: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
return _deviceKeyToHandleMap.ExecuteWithLock(dict =>
|
return _deviceKeyToHandleMap.ExecuteWithLock(dict =>
|
||||||
{
|
{
|
||||||
// Try to reuse existing handle if it's still valid
|
// Try to reuse existing handle if it's still valid
|
||||||
|
// Use quick connection check instead of full capabilities retrieval
|
||||||
if (dict.TryGetValue(deviceKey, out var existingHandle) &&
|
if (dict.TryGetValue(deviceKey, out var existingHandle) &&
|
||||||
existingHandle != IntPtr.Zero &&
|
existingHandle != IntPtr.Zero &&
|
||||||
DdcCiNative.ValidateDdcCiConnection(existingHandle))
|
DdcCiNative.QuickConnectionCheck(existingHandle))
|
||||||
{
|
{
|
||||||
// Destroy the newly created handle since we're using the old one
|
// Destroy the newly created handle since we're using the old one
|
||||||
if (newHandle != existingHandle && newHandle != IntPtr.Zero)
|
if (newHandle != existingHandle && newHandle != IntPtr.Zero)
|
||||||
|
|||||||
@@ -157,10 +157,19 @@ namespace PowerDisplay.Core
|
|||||||
IMonitorController controller,
|
IMonitorController controller,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Verify if monitor can be controlled
|
// Skip control verification if monitor was already validated during discovery phase
|
||||||
if (!await controller.CanControlMonitorAsync(monitor, cancellationToken))
|
// The presence of cached VcpCapabilitiesInfo indicates the monitor passed DDC/CI validation
|
||||||
|
// This avoids redundant capabilities retrieval (~4 seconds per monitor)
|
||||||
|
bool alreadyValidated = monitor.VcpCapabilitiesInfo != null &&
|
||||||
|
monitor.VcpCapabilitiesInfo.SupportedVcpCodes.Count > 0;
|
||||||
|
|
||||||
|
if (!alreadyValidated)
|
||||||
{
|
{
|
||||||
return null;
|
// Verify if monitor can be controlled (for monitors not validated in discovery phase)
|
||||||
|
if (!await controller.CanControlMonitorAsync(monitor, cancellationToken))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current brightness
|
// Get current brightness
|
||||||
@@ -207,6 +216,7 @@ namespace PowerDisplay.Core
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize monitor DDC/CI capabilities.
|
/// Initialize monitor DDC/CI capabilities.
|
||||||
|
/// If capabilities are already cached from discovery phase, only update derived properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task InitializeMonitorCapabilitiesAsync(
|
private async Task InitializeMonitorCapabilitiesAsync(
|
||||||
Monitor monitor,
|
Monitor monitor,
|
||||||
@@ -215,6 +225,15 @@ namespace PowerDisplay.Core
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Check if capabilities were already cached during discovery phase
|
||||||
|
// This avoids expensive I2C calls (~4 seconds per monitor) for redundant data
|
||||||
|
if (monitor.VcpCapabilitiesInfo != null && monitor.VcpCapabilitiesInfo.SupportedVcpCodes.Count > 0)
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"Using cached capabilities for {monitor.Id}: {monitor.VcpCapabilitiesInfo.SupportedVcpCodes.Count} VCP codes");
|
||||||
|
UpdateMonitorCapabilitiesFromVcp(monitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.LogInfo($"Getting capabilities for monitor {monitor.Id}");
|
Logger.LogInfo($"Getting capabilities for monitor {monitor.Id}");
|
||||||
var capsString = await controller.GetCapabilitiesStringAsync(monitor, cancellationToken);
|
var capsString = await controller.GetCapabilitiesStringAsync(monitor, cancellationToken);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user