mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
Unify monitor identification using stable Id property
Refactor monitor matching and persistence to use a single, stable Id (format: "{Source}_{EdidId}_{MonitorNumber}") across all components. Remove HardwareId and DeviceKey properties from Monitor, update ProfileMonitorSetting to use MonitorId, and simplify MonitorMatchingHelper logic. Update DDC/CI, WMI, ViewModels, Settings UI, and unit tests to rely on Id for all lookups, state management, and handle mapping. Improves reliability for multi-monitor setups and simplifies codebase by removing legacy fallback logic.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using PowerDisplay.Common.Models;
|
||||||
using PowerDisplay.Common.Utils;
|
using PowerDisplay.Common.Utils;
|
||||||
|
|
||||||
namespace PowerDisplay.UnitTests;
|
namespace PowerDisplay.UnitTests;
|
||||||
@@ -15,72 +16,79 @@ namespace PowerDisplay.UnitTests;
|
|||||||
public class MonitorMatchingHelperTests
|
public class MonitorMatchingHelperTests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetMonitorKey_WithHardwareId_ReturnsHardwareId()
|
public void GetMonitorKey_WithMonitor_ReturnsId()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var hardwareId = "HW_ID_123";
|
var monitor = new Monitor { Id = "DDC_GSM5C6D_1", Name = "LG Monitor" };
|
||||||
var internalName = "Internal_Name";
|
|
||||||
var name = "Display Name";
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = MonitorMatchingHelper.GetMonitorKey(hardwareId, internalName, name);
|
var result = MonitorMatchingHelper.GetMonitorKey(monitor);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.AreEqual(hardwareId, result);
|
Assert.AreEqual("DDC_GSM5C6D_1", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetMonitorKey_NoHardwareId_ReturnsInternalName()
|
public void GetMonitorKey_NullMonitor_ReturnsEmptyString()
|
||||||
{
|
{
|
||||||
// Arrange
|
|
||||||
string? hardwareId = null;
|
|
||||||
var internalName = "Internal_Name";
|
|
||||||
var name = "Display Name";
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = MonitorMatchingHelper.GetMonitorKey(hardwareId, internalName, name);
|
var result = MonitorMatchingHelper.GetMonitorKey(null);
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.AreEqual(internalName, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void GetMonitorKey_NoHardwareIdOrInternalName_ReturnsName()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
string? hardwareId = null;
|
|
||||||
string? internalName = null;
|
|
||||||
var name = "Display Name";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = MonitorMatchingHelper.GetMonitorKey(hardwareId, internalName, name);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.AreEqual(name, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void GetMonitorKey_AllNull_ReturnsEmptyString()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var result = MonitorMatchingHelper.GetMonitorKey(null, null, null);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.AreEqual(string.Empty, result);
|
Assert.AreEqual(string.Empty, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void GetMonitorKey_EmptyHardwareId_FallsBackToInternalName()
|
public void GetMonitorKey_EmptyId_ReturnsEmptyString()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var hardwareId = string.Empty;
|
var monitor = new Monitor { Id = string.Empty, Name = "Display Name" };
|
||||||
var internalName = "Internal_Name";
|
|
||||||
var name = "Display Name";
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = MonitorMatchingHelper.GetMonitorKey(hardwareId, internalName, name);
|
var result = MonitorMatchingHelper.GetMonitorKey(monitor);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.AreEqual(internalName, result);
|
Assert.AreEqual(string.Empty, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void AreMonitorsSame_SameId_ReturnsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var monitor1 = new Monitor { Id = "DDC_GSM5C6D_1", Name = "Monitor 1" };
|
||||||
|
var monitor2 = new Monitor { Id = "DDC_GSM5C6D_1", Name = "Monitor 2" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = MonitorMatchingHelper.AreMonitorsSame(monitor1, monitor2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void AreMonitorsSame_DifferentId_ReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var monitor1 = new Monitor { Id = "DDC_GSM5C6D_1", Name = "Monitor 1" };
|
||||||
|
var monitor2 = new Monitor { Id = "DDC_GSM5C6D_2", Name = "Monitor 2" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = MonitorMatchingHelper.AreMonitorsSame(monitor1, monitor2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void AreMonitorsSame_NullMonitor_ReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var monitor1 = new Monitor { Id = "DDC_GSM5C6D_1", Name = "Monitor 1" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = MonitorMatchingHelper.AreMonitorsSame(monitor1, null!);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsFalse(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,12 +492,12 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
|
|
||||||
var monitorInfo = matchingInfos[i];
|
var monitorInfo = matchingInfos[i];
|
||||||
|
|
||||||
// Generate stable device key using DevicePath hash for uniqueness
|
// Generate unique monitor Id
|
||||||
var deviceKey = !string.IsNullOrEmpty(monitorInfo.HardwareId)
|
var monitorId = !string.IsNullOrEmpty(monitorInfo.HardwareId)
|
||||||
? $"{monitorInfo.HardwareId}_{monitorInfo.MonitorNumber}"
|
? $"DDC_{monitorInfo.HardwareId}_{monitorInfo.MonitorNumber}"
|
||||||
: $"Unknown_{monitorInfo.MonitorNumber}";
|
: $"DDC_Unknown_{monitorInfo.MonitorNumber}";
|
||||||
|
|
||||||
var (handleToUse, _) = _handleManager.ReuseOrCreateHandle(deviceKey, physicalMonitor.HPhysicalMonitor);
|
var (handleToUse, _) = _handleManager.ReuseOrCreateHandle(monitorId, physicalMonitor.HPhysicalMonitor);
|
||||||
|
|
||||||
var monitorToCreate = physicalMonitor;
|
var monitorToCreate = physicalMonitor;
|
||||||
monitorToCreate.HPhysicalMonitor = handleToUse;
|
monitorToCreate.HPhysicalMonitor = handleToUse;
|
||||||
@@ -585,7 +585,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
monitors.Add(monitor);
|
monitors.Add(monitor);
|
||||||
newHandleMap[monitor.DeviceKey] = candidate.Handle;
|
newHandleMap[monitor.Id] = candidate.Handle;
|
||||||
|
|
||||||
Logger.LogInfo($"DDC: Added monitor {monitor.Id} with {monitor.VcpCapabilitiesInfo?.SupportedVcpCodes.Count ?? 0} VCP codes");
|
Logger.LogInfo($"DDC: Added monitor {monitor.Id} with {monitor.VcpCapabilitiesInfo?.SupportedVcpCodes.Count ?? 0} VCP codes");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get hardware ID and friendly name directly from MonitorDisplayInfo
|
// Get hardware ID and friendly name directly from MonitorDisplayInfo
|
||||||
string hardwareId = monitorInfo.HardwareId ?? string.Empty;
|
string edidId = monitorInfo.HardwareId ?? string.Empty;
|
||||||
string name = physicalMonitor.GetDescription() ?? string.Empty;
|
string name = physicalMonitor.GetDescription() ?? string.Empty;
|
||||||
|
|
||||||
// Use FriendlyName from QueryDisplayConfig if available and not generic
|
// Use FriendlyName from QueryDisplayConfig if available and not generic
|
||||||
@@ -141,12 +141,10 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
name = monitorInfo.FriendlyName;
|
name = monitorInfo.FriendlyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate stable device key: "{HardwareId}_{MonitorNumber}"
|
// Generate unique monitor Id: "DDC_{EdidId}_{MonitorNumber}"
|
||||||
string deviceKey = !string.IsNullOrEmpty(hardwareId)
|
string monitorId = !string.IsNullOrEmpty(edidId)
|
||||||
? $"{hardwareId}_{monitorInfo.MonitorNumber}"
|
? $"DDC_{edidId}_{monitorInfo.MonitorNumber}"
|
||||||
: $"Unknown_{monitorInfo.MonitorNumber}";
|
: $"DDC_Unknown_{monitorInfo.MonitorNumber}";
|
||||||
|
|
||||||
string monitorId = $"DDC_{deviceKey}";
|
|
||||||
|
|
||||||
// If still no good name, use default value
|
// If still no good name, use default value
|
||||||
if (string.IsNullOrEmpty(name) || name.Contains("Generic") || name.Contains("PnP"))
|
if (string.IsNullOrEmpty(name) || name.Contains("Generic") || name.Contains("PnP"))
|
||||||
@@ -160,14 +158,12 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
var monitor = new Monitor
|
var monitor = new Monitor
|
||||||
{
|
{
|
||||||
Id = monitorId,
|
Id = monitorId,
|
||||||
HardwareId = hardwareId,
|
|
||||||
Name = name.Trim(),
|
Name = name.Trim(),
|
||||||
CurrentBrightness = brightnessInfo.IsValid ? brightnessInfo.ToPercentage() : 50,
|
CurrentBrightness = brightnessInfo.IsValid ? brightnessInfo.ToPercentage() : 50,
|
||||||
MinBrightness = 0,
|
MinBrightness = 0,
|
||||||
MaxBrightness = 100,
|
MaxBrightness = 100,
|
||||||
IsAvailable = true,
|
IsAvailable = true,
|
||||||
Handle = physicalMonitor.HPhysicalMonitor,
|
Handle = physicalMonitor.HPhysicalMonitor,
|
||||||
DeviceKey = deviceKey,
|
|
||||||
Capabilities = MonitorCapabilities.DdcCi,
|
Capabilities = MonitorCapabilities.DdcCi,
|
||||||
ConnectionType = "External",
|
ConnectionType = "External",
|
||||||
CommunicationMethod = "DDC/CI",
|
CommunicationMethod = "DDC/CI",
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PhysicalMonitorHandleManager : IDisposable
|
public partial class PhysicalMonitorHandleManager : IDisposable
|
||||||
{
|
{
|
||||||
// Mapping: deviceKey -> physical handle (thread-safe)
|
// Mapping: monitorId -> physical handle (thread-safe)
|
||||||
private readonly LockedDictionary<string, IntPtr> _deviceKeyToHandleMap = new();
|
private readonly LockedDictionary<string, IntPtr> _monitorIdToHandleMap = new();
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get physical handle for monitor using stable deviceKey
|
/// Get physical handle for monitor using its unique Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr GetPhysicalHandle(Monitor monitor)
|
public IntPtr GetPhysicalHandle(Monitor monitor)
|
||||||
{
|
{
|
||||||
// Primary lookup: use stable deviceKey from EnumDisplayDevices
|
// Primary lookup: use monitor Id
|
||||||
if (!string.IsNullOrEmpty(monitor.DeviceKey) &&
|
if (!string.IsNullOrEmpty(monitor.Id) &&
|
||||||
_deviceKeyToHandleMap.TryGetValue(monitor.DeviceKey, out var handle))
|
_monitorIdToHandleMap.TryGetValue(monitor.Id, out var handle))
|
||||||
{
|
{
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
@@ -45,18 +45,18 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
/// Try to reuse existing handle if valid; otherwise uses new handle
|
/// Try to reuse existing handle if valid; otherwise uses new handle
|
||||||
/// Returns the handle to use and whether it was reused
|
/// Returns the handle to use and whether it was reused
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public (IntPtr Handle, bool WasReused) ReuseOrCreateHandle(string deviceKey, IntPtr newHandle)
|
public (IntPtr Handle, bool WasReused) ReuseOrCreateHandle(string monitorId, IntPtr newHandle)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(deviceKey))
|
if (string.IsNullOrEmpty(monitorId))
|
||||||
{
|
{
|
||||||
return (newHandle, false);
|
return (newHandle, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _deviceKeyToHandleMap.ExecuteWithLock(dict =>
|
return _monitorIdToHandleMap.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
|
// Use quick connection check instead of full capabilities retrieval
|
||||||
if (dict.TryGetValue(deviceKey, out var existingHandle) &&
|
if (dict.TryGetValue(monitorId, out var existingHandle) &&
|
||||||
existingHandle != IntPtr.Zero &&
|
existingHandle != IntPtr.Zero &&
|
||||||
DdcCiNative.QuickConnectionCheck(existingHandle))
|
DdcCiNative.QuickConnectionCheck(existingHandle))
|
||||||
{
|
{
|
||||||
@@ -78,7 +78,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateHandleMap(Dictionary<string, IntPtr> newHandleMap)
|
public void UpdateHandleMap(Dictionary<string, IntPtr> newHandleMap)
|
||||||
{
|
{
|
||||||
_deviceKeyToHandleMap.ExecuteWithLock(dict =>
|
_monitorIdToHandleMap.ExecuteWithLock(dict =>
|
||||||
{
|
{
|
||||||
// Clean up unused handles before updating
|
// Clean up unused handles before updating
|
||||||
CleanupUnusedHandles(dict, newHandleMap);
|
CleanupUnusedHandles(dict, newHandleMap);
|
||||||
@@ -140,7 +140,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Release all physical monitor handles - get snapshot to avoid holding lock during cleanup
|
// Release all physical monitor handles - get snapshot to avoid holding lock during cleanup
|
||||||
var handles = _deviceKeyToHandleMap.GetValuesSnapshot();
|
var handles = _monitorIdToHandleMap.GetValuesSnapshot();
|
||||||
foreach (var handle in handles)
|
foreach (var handle in handles)
|
||||||
{
|
{
|
||||||
if (handle != IntPtr.Zero)
|
if (handle != IntPtr.Zero)
|
||||||
@@ -157,7 +157,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceKeyToHandleMap.Clear();
|
_monitorIdToHandleMap.Clear();
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,19 +354,23 @@ namespace PowerDisplay.Common.Drivers.WMI
|
|||||||
name = info.Name;
|
name = info.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract HardwareId from InstanceName for state persistence
|
// Extract EdidId from InstanceName
|
||||||
// e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "BOE0900"
|
// e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "BOE0900"
|
||||||
var hardwareId = ExtractHardwareIdFromInstanceName(instanceName);
|
var edidId = ExtractHardwareIdFromInstanceName(instanceName);
|
||||||
|
|
||||||
// Get MonitorNumber from QueryDisplayConfig by matching HardwareId
|
// Get MonitorNumber from QueryDisplayConfig by matching EdidId
|
||||||
// This matches Windows Display Settings "Identify" feature
|
// This matches Windows Display Settings "Identify" feature
|
||||||
int monitorNumber = GetMonitorNumberFromDisplayInfo(hardwareId, monitorDisplayInfos);
|
int monitorNumber = GetMonitorNumberFromDisplayInfo(edidId, monitorDisplayInfos);
|
||||||
|
|
||||||
|
// Generate unique monitor Id: "WMI_{EdidId}_{MonitorNumber}"
|
||||||
|
string monitorId = !string.IsNullOrEmpty(edidId)
|
||||||
|
? $"WMI_{edidId}_{monitorNumber}"
|
||||||
|
: $"WMI_Unknown_{monitorNumber}";
|
||||||
|
|
||||||
var monitor = new Monitor
|
var monitor = new Monitor
|
||||||
{
|
{
|
||||||
Id = $"WMI_{instanceName}",
|
Id = monitorId,
|
||||||
Name = name,
|
Name = name,
|
||||||
HardwareId = hardwareId,
|
|
||||||
CurrentBrightness = currentBrightness,
|
CurrentBrightness = currentBrightness,
|
||||||
MinBrightness = 0,
|
MinBrightness = 0,
|
||||||
MaxBrightness = 100,
|
MaxBrightness = 100,
|
||||||
@@ -375,7 +379,7 @@ namespace PowerDisplay.Common.Drivers.WMI
|
|||||||
Capabilities = MonitorCapabilities.Brightness | MonitorCapabilities.Wmi,
|
Capabilities = MonitorCapabilities.Brightness | MonitorCapabilities.Wmi,
|
||||||
ConnectionType = "Internal",
|
ConnectionType = "Internal",
|
||||||
CommunicationMethod = "WMI",
|
CommunicationMethod = "WMI",
|
||||||
Manufacturer = hardwareId.Length >= 3 ? hardwareId.Substring(0, 3) : "Internal",
|
Manufacturer = edidId.Length >= 3 ? edidId.Substring(0, 3) : "Internal",
|
||||||
SupportsColorTemperature = false,
|
SupportsColorTemperature = false,
|
||||||
MonitorNumber = monitorNumber,
|
MonitorNumber = monitorNumber,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ namespace PowerDisplay.Common.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string Name { get; set; }
|
string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the hardware ID (EDID format like GSM5C6D).
|
|
||||||
/// </summary>
|
|
||||||
string HardwareId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the current brightness value (0-100).
|
/// Gets or sets the current brightness value (0-100).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,13 +15,8 @@ namespace PowerDisplay.Common.Models
|
|||||||
/// Implements IMonitorData to provide a common interface for monitor hardware values.
|
/// Implements IMonitorData to provide a common interface for monitor hardware values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para><b>Monitor Identifier Hierarchy:</b></para>
|
/// <para><see cref="Id"/> is the unique identifier used for all purposes: UI lookups, IPC, persistent storage, and handle management.</para>
|
||||||
/// <list type="bullet">
|
/// <para>Format: "{Source}_{EdidId}_{MonitorNumber}" (e.g., "DDC_GSM5C6D_1", "WMI_BOE0900_2").</para>
|
||||||
/// <item><see cref="Id"/>: Runtime identifier for UI and IPC (e.g., "DDC_GSM5C6D", "WMI_DISPLAY\BOE...")</item>
|
|
||||||
/// <item><see cref="HardwareId"/>: EDID-based identifier for persistent storage (e.g., "GSM5C6D")</item>
|
|
||||||
/// <item><see cref="DeviceKey"/>: Windows device path for handle management (e.g., "\\?\DISPLAY#...")</item>
|
|
||||||
/// </list>
|
|
||||||
/// <para>Use <see cref="Id"/> for lookups, <see cref="HardwareId"/> for saving state, <see cref="DeviceKey"/> for handle reuse.</para>
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public partial class Monitor : INotifyPropertyChanged, IMonitorData
|
public partial class Monitor : INotifyPropertyChanged, IMonitorData
|
||||||
{
|
{
|
||||||
@@ -31,25 +26,15 @@ namespace PowerDisplay.Common.Models
|
|||||||
private bool _isAvailable = true;
|
private bool _isAvailable = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runtime unique identifier for UI lookups and IPC communication.
|
/// Unique identifier for all purposes: UI lookups, IPC, persistent storage, and handle management.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Format: "{Source}_{HardwareId}" where Source is "DDC" or "WMI".
|
/// Format: "{Source}_{EdidId}_{MonitorNumber}" where Source is "DDC" or "WMI".
|
||||||
/// Examples: "DDC_GSM5C6D", "WMI_DISPLAY\BOE0900...".
|
/// Examples: "DDC_GSM5C6D_1", "WMI_BOE0900_2".
|
||||||
/// Use this for ViewModel lookups and MonitorManager method parameters.
|
/// Stable across reboots and unique even for multiple identical monitors.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public string Id { get; set; } = string.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// EDID-based hardware identifier for persistent state storage.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Format: Manufacturer code + product code from EDID (e.g., "GSM5C6D" for LG monitors).
|
|
||||||
/// Use this for saving/loading monitor settings in MonitorStateManager.
|
|
||||||
/// Stable across reboots but not guaranteed unique if multiple identical monitors are connected.
|
|
||||||
/// </remarks>
|
|
||||||
public string HardwareId { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Display name
|
/// Display name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -242,17 +227,6 @@ namespace PowerDisplay.Common.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr Handle { get; set; } = IntPtr.Zero;
|
public IntPtr Handle { get; set; } = IntPtr.Zero;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stable device key for persistent monitor identification and handle management.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Format: "{HardwareId}_{MonitorNumber}" (e.g., "GSM5C6D_1").
|
|
||||||
/// HardwareId is from EDID (manufacturer+product code), MonitorNumber from QueryDisplayConfig.
|
|
||||||
/// Used by PhysicalMonitorHandleManager to reuse handles across monitor discovery cycles.
|
|
||||||
/// Same-model monitors on different ports will have different keys due to MonitorNumber.
|
|
||||||
/// </remarks>
|
|
||||||
public string DeviceKey { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instance name (used by WMI)
|
/// Instance name (used by WMI)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ namespace PowerDisplay.Common.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProfileMonitorSetting
|
public class ProfileMonitorSetting
|
||||||
{
|
{
|
||||||
[JsonPropertyName("hardwareId")]
|
/// <summary>
|
||||||
public string HardwareId { get; set; }
|
/// Gets or sets the monitor's unique identifier.
|
||||||
|
/// Format: "{Source}_{EdidId}_{MonitorNumber}" (e.g., "DDC_GSM5C6D_1").
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("monitorId")]
|
||||||
|
public string MonitorId { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("brightness")]
|
[JsonPropertyName("brightness")]
|
||||||
public int? Brightness { get; set; }
|
public int? Brightness { get; set; }
|
||||||
@@ -30,23 +34,18 @@ namespace PowerDisplay.Common.Models
|
|||||||
[JsonPropertyName("colorTemperature")]
|
[JsonPropertyName("colorTemperature")]
|
||||||
public int? ColorTemperatureVcp { get; set; }
|
public int? ColorTemperatureVcp { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("monitorInternalName")]
|
|
||||||
public string MonitorInternalName { get; set; }
|
|
||||||
|
|
||||||
public ProfileMonitorSetting()
|
public ProfileMonitorSetting()
|
||||||
{
|
{
|
||||||
HardwareId = string.Empty;
|
MonitorId = string.Empty;
|
||||||
MonitorInternalName = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileMonitorSetting(string hardwareId, int? brightness = null, int? colorTemperatureVcp = null, int? contrast = null, int? volume = null, string monitorInternalName = "")
|
public ProfileMonitorSetting(string monitorId, int? brightness = null, int? colorTemperatureVcp = null, int? contrast = null, int? volume = null)
|
||||||
{
|
{
|
||||||
HardwareId = hardwareId;
|
MonitorId = monitorId;
|
||||||
Brightness = brightness;
|
Brightness = brightness;
|
||||||
ColorTemperatureVcp = colorTemperatureVcp;
|
ColorTemperatureVcp = colorTemperatureVcp;
|
||||||
Contrast = contrast;
|
Contrast = contrast;
|
||||||
Volume = volume;
|
Volume = volume;
|
||||||
MonitorInternalName = monitorInternalName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,33 +14,19 @@ namespace PowerDisplay.Common.Utils
|
|||||||
public static class MonitorMatchingHelper
|
public static class MonitorMatchingHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a unique key for monitor matching based on hardware ID and internal name.
|
/// Generate a unique key for monitor matching based on Id.
|
||||||
/// Uses HardwareId if available; otherwise falls back to Id (InternalName) or Name.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="monitor">The monitor data to generate a key for.</param>
|
/// <param name="monitor">The monitor data to generate a key for.</param>
|
||||||
/// <returns>A unique string key for the monitor.</returns>
|
/// <returns>A unique string key for the monitor.</returns>
|
||||||
public static string GetMonitorKey(IMonitorData? monitor)
|
public static string GetMonitorKey(IMonitorData? monitor)
|
||||||
=> GetMonitorKey(monitor?.HardwareId, monitor?.Id, monitor?.Name);
|
=> monitor?.Id ?? string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a unique key for monitor matching using explicit values.
|
/// Check if two monitors are considered the same based on their Ids.
|
||||||
/// Uses priority: HardwareId > InternalName > Name.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hardwareId">The monitor's hardware ID.</param>
|
|
||||||
/// <param name="internalName">The monitor's internal name (optional fallback).</param>
|
|
||||||
/// <param name="name">The monitor's display name (optional fallback).</param>
|
|
||||||
/// <returns>A unique string key for the monitor.</returns>
|
|
||||||
public static string GetMonitorKey(string? hardwareId, string? internalName = null, string? name = null)
|
|
||||||
=> !string.IsNullOrEmpty(hardwareId) ? hardwareId
|
|
||||||
: !string.IsNullOrEmpty(internalName) ? internalName
|
|
||||||
: name ?? string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if two monitors are considered the same based on their keys.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="monitor1">First monitor.</param>
|
/// <param name="monitor1">First monitor.</param>
|
||||||
/// <param name="monitor2">Second monitor.</param>
|
/// <param name="monitor2">Second monitor.</param>
|
||||||
/// <returns>True if the monitors have the same key.</returns>
|
/// <returns>True if the monitors have the same Id.</returns>
|
||||||
public static bool AreMonitorsSame(IMonitorData monitor1, IMonitorData monitor2)
|
public static bool AreMonitorsSame(IMonitorData monitor1, IMonitorData monitor2)
|
||||||
{
|
{
|
||||||
if (monitor1 == null || monitor2 == null)
|
if (monitor1 == null || monitor2 == null)
|
||||||
@@ -48,7 +34,7 @@ namespace PowerDisplay.Common.Utils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetMonitorKey(monitor1) == GetMonitorKey(monitor2);
|
return !string.IsNullOrEmpty(monitor1.Id) && monitor1.Id == monitor2.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,16 +254,16 @@ public partial class MainViewModel
|
|||||||
|
|
||||||
foreach (var setting in monitorSettings)
|
foreach (var setting in monitorSettings)
|
||||||
{
|
{
|
||||||
// Find monitor by InternalName (unique identifier)
|
// Find monitor by Id (unique identifier)
|
||||||
var monitorVm = Monitors.FirstOrDefault(m => m.InternalName == setting.MonitorInternalName);
|
var monitorVm = Monitors.FirstOrDefault(m => m.Id == setting.MonitorId);
|
||||||
|
|
||||||
if (monitorVm == null)
|
if (monitorVm == null)
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[Profile] Monitor with InternalName '{setting.MonitorInternalName}' not found (disconnected?)");
|
Logger.LogWarning($"[Profile] Monitor with Id '{setting.MonitorId}' not found (disconnected?)");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInfo($"[Profile] Applying settings to monitor '{monitorVm.Name}' (InternalName: {setting.MonitorInternalName}, HardwareId: {setting.HardwareId})");
|
Logger.LogInfo($"[Profile] Applying settings to monitor '{monitorVm.Name}' (Id: {setting.MonitorId})");
|
||||||
|
|
||||||
// Apply brightness if included in profile
|
// Apply brightness if included in profile
|
||||||
if (setting.Brightness.HasValue &&
|
if (setting.Brightness.HasValue &&
|
||||||
@@ -312,8 +312,8 @@ public partial class MainViewModel
|
|||||||
|
|
||||||
foreach (var monitorVm in Monitors)
|
foreach (var monitorVm in Monitors)
|
||||||
{
|
{
|
||||||
// Find and apply corresponding saved settings from state file using stable HardwareId
|
// Find and apply corresponding saved settings from state file using unique monitor Id
|
||||||
var savedState = _stateManager.GetMonitorParameters(monitorVm.HardwareId);
|
var savedState = _stateManager.GetMonitorParameters(monitorVm.Id);
|
||||||
if (!savedState.HasValue)
|
if (!savedState.HasValue)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -384,18 +384,18 @@ public partial class MainViewModel
|
|||||||
/// Thread-safe save method that can be called from background threads.
|
/// Thread-safe save method that can be called from background threads.
|
||||||
/// Does not access UI collections or update UI properties.
|
/// Does not access UI collections or update UI properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SaveMonitorSettingDirect(string hardwareId, string property, int value)
|
public void SaveMonitorSettingDirect(string monitorId, string property, int value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// This is thread-safe - _stateManager has internal locking
|
// This is thread-safe - _stateManager has internal locking
|
||||||
// No UI thread operations, no ObservableCollection access
|
// No UI thread operations, no ObservableCollection access
|
||||||
_stateManager.UpdateMonitorParameter(hardwareId, property, value);
|
_stateManager.UpdateMonitorParameter(monitorId, property, value);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Only log, don't update UI from background thread
|
// Only log, don't update UI from background thread
|
||||||
Logger.LogError($"Failed to queue setting save for HardwareId '{hardwareId}': {ex.Message}");
|
Logger.LogError($"Failed to queue setting save for monitorId '{monitorId}': {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ public partial class MainViewModel
|
|||||||
var monitorInfo = new Microsoft.PowerToys.Settings.UI.Library.MonitorInfo(
|
var monitorInfo = new Microsoft.PowerToys.Settings.UI.Library.MonitorInfo(
|
||||||
name: vm.Name,
|
name: vm.Name,
|
||||||
internalName: vm.Id,
|
internalName: vm.Id,
|
||||||
hardwareId: vm.HardwareId,
|
hardwareId: string.Empty, // Deprecated, use InternalName (Id) instead
|
||||||
communicationMethod: vm.CommunicationMethod,
|
communicationMethod: vm.CommunicationMethod,
|
||||||
currentBrightness: vm.Brightness,
|
currentBrightness: vm.Brightness,
|
||||||
colorTemperatureVcp: vm.ColorTemperature)
|
colorTemperatureVcp: vm.ColorTemperature)
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInfo($"[{HardwareId}] Setting color temperature to 0x{colorTemperature:X2}");
|
Logger.LogInfo($"[{Id}] Setting color temperature to 0x{colorTemperature:X2}");
|
||||||
|
|
||||||
var result = await _monitorManager.SetColorTemperatureAsync(Id, colorTemperature);
|
var result = await _monitorManager.SetColorTemperatureAsync(Id, colorTemperature);
|
||||||
|
|
||||||
@@ -173,18 +173,18 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
OnPropertyChanged(nameof(ColorTemperature));
|
OnPropertyChanged(nameof(ColorTemperature));
|
||||||
OnPropertyChanged(nameof(ColorTemperaturePresetName));
|
OnPropertyChanged(nameof(ColorTemperaturePresetName));
|
||||||
|
|
||||||
_mainViewModel?.SaveMonitorSettingDirect(_monitor.HardwareId, nameof(ColorTemperature), colorTemperature);
|
_mainViewModel?.SaveMonitorSettingDirect(_monitor.Id, nameof(ColorTemperature), colorTemperature);
|
||||||
|
|
||||||
Logger.LogInfo($"[{HardwareId}] Color temperature applied successfully");
|
Logger.LogInfo($"[{Id}] Color temperature applied successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[{HardwareId}] Failed to set color temperature: {result.ErrorMessage}");
|
Logger.LogWarning($"[{Id}] Failed to set color temperature: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError($"[{HardwareId}] Exception setting color temperature: {ex.Message}");
|
Logger.LogError($"[{Id}] Exception setting color temperature: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,22 +202,22 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogDebug($"[{HardwareId}] Applying {propertyName.ToLowerInvariant()}: {value}%");
|
Logger.LogDebug($"[{Id}] Applying {propertyName.ToLowerInvariant()}: {value}%");
|
||||||
|
|
||||||
var result = await setAsyncFunc(Id, value, default);
|
var result = await setAsyncFunc(Id, value, default);
|
||||||
|
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
_mainViewModel?.SaveMonitorSettingDirect(_monitor.HardwareId, propertyName, value);
|
_mainViewModel?.SaveMonitorSettingDirect(_monitor.Id, propertyName, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[{HardwareId}] Failed to set {propertyName.ToLowerInvariant()}: {result.ErrorMessage}");
|
Logger.LogWarning($"[{Id}] Failed to set {propertyName.ToLowerInvariant()}: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError($"[{HardwareId}] Exception setting {propertyName.ToLowerInvariant()}: {ex.Message}");
|
Logger.LogError($"[{Id}] Exception setting {propertyName.ToLowerInvariant()}: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,8 +266,6 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
|
|
||||||
public string Id => _monitor.Id;
|
public string Id => _monitor.Id;
|
||||||
|
|
||||||
public string HardwareId => _monitor.HardwareId;
|
|
||||||
|
|
||||||
public string InternalName => _monitor.Id;
|
public string InternalName => _monitor.Id;
|
||||||
|
|
||||||
public string Name => _monitor.Name;
|
public string Name => _monitor.Name;
|
||||||
@@ -373,7 +371,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether to show rotation controls (controlled by Settings UI, default false)
|
/// Gets or sets a value indicating whether gets or sets whether to show rotation controls (controlled by Settings UI, default false)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ShowRotation
|
public bool ShowRotation
|
||||||
{
|
{
|
||||||
@@ -394,22 +392,22 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
public int CurrentRotation => _monitor.Orientation;
|
public int CurrentRotation => _monitor.Orientation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the current rotation is 0° (normal/default)
|
/// Gets a value indicating whether gets whether the current rotation is 0° (normal/default)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRotation0 => CurrentRotation == 0;
|
public bool IsRotation0 => CurrentRotation == 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the current rotation is 90° (rotated right)
|
/// Gets a value indicating whether gets whether the current rotation is 90° (rotated right)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRotation1 => CurrentRotation == 1;
|
public bool IsRotation1 => CurrentRotation == 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the current rotation is 180° (inverted)
|
/// Gets a value indicating whether gets whether the current rotation is 180° (inverted)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRotation2 => CurrentRotation == 2;
|
public bool IsRotation2 => CurrentRotation == 2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the current rotation is 270° (rotated left)
|
/// Gets a value indicating whether gets whether the current rotation is 270° (rotated left)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRotation3 => CurrentRotation == 3;
|
public bool IsRotation3 => CurrentRotation == 3;
|
||||||
|
|
||||||
@@ -422,7 +420,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
// Validate orientation range (0=normal, 1=90°, 2=180°, 3=270°)
|
// Validate orientation range (0=normal, 1=90°, 2=180°, 3=270°)
|
||||||
if (orientation < 0 || orientation > 3)
|
if (orientation < 0 || orientation > 3)
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[{HardwareId}] Invalid rotation value: {orientation}. Must be 0-3.");
|
Logger.LogWarning($"[{Id}] Invalid rotation value: {orientation}. Must be 0-3.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +432,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInfo($"[{HardwareId}] Setting rotation to {orientation}");
|
Logger.LogInfo($"[{Id}] Setting rotation to {orientation}");
|
||||||
|
|
||||||
var result = await _monitorManager.SetRotationAsync(Id, orientation);
|
var result = await _monitorManager.SetRotationAsync(Id, orientation);
|
||||||
|
|
||||||
@@ -447,16 +445,16 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
OnPropertyChanged(nameof(IsRotation2));
|
OnPropertyChanged(nameof(IsRotation2));
|
||||||
OnPropertyChanged(nameof(IsRotation3));
|
OnPropertyChanged(nameof(IsRotation3));
|
||||||
|
|
||||||
Logger.LogInfo($"[{HardwareId}] Rotation set successfully to {orientation}");
|
Logger.LogInfo($"[{Id}] Rotation set successfully to {orientation}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[{HardwareId}] Failed to set rotation: {result.ErrorMessage}");
|
Logger.LogWarning($"[{Id}] Failed to set rotation: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError($"[{HardwareId}] Exception setting rotation: {ex.Message}");
|
Logger.LogError($"[{Id}] Exception setting rotation: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,7 +484,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
public string ColorTemperaturePresetName => _monitor.ColorTemperaturePresetName;
|
public string ColorTemperaturePresetName => _monitor.ColorTemperaturePresetName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this monitor supports input source switching via VCP 0x60
|
/// Gets a value indicating whether whether this monitor supports input source switching via VCP 0x60
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsInputSource => _monitor.SupportsInputSource;
|
public bool SupportsInputSource => _monitor.SupportsInputSource;
|
||||||
|
|
||||||
@@ -548,7 +546,7 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInfo($"[{HardwareId}] Setting input source to 0x{inputSource:X2}");
|
Logger.LogInfo($"[{Id}] Setting input source to 0x{inputSource:X2}");
|
||||||
|
|
||||||
var result = await _monitorManager.SetInputSourceAsync(Id, inputSource);
|
var result = await _monitorManager.SetInputSourceAsync(Id, inputSource);
|
||||||
|
|
||||||
@@ -558,16 +556,16 @@ public partial class MonitorViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
OnPropertyChanged(nameof(CurrentInputSourceName));
|
OnPropertyChanged(nameof(CurrentInputSourceName));
|
||||||
RefreshAvailableInputSources();
|
RefreshAvailableInputSources();
|
||||||
|
|
||||||
Logger.LogInfo($"[{HardwareId}] Input source set successfully to {CurrentInputSourceName}");
|
Logger.LogInfo($"[{Id}] Input source set successfully to {CurrentInputSourceName}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[{HardwareId}] Failed to set input source: {result.ErrorMessage}");
|
Logger.LogWarning($"[{Id}] Failed to set input source: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError($"[{HardwareId}] Exception setting input source: {ex.Message}");
|
Logger.LogError($"[{Id}] Exception setting input source: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||||||
// Pre-fill monitor settings from existing profile
|
// Pre-fill monitor settings from existing profile
|
||||||
foreach (var monitorSetting in profile.MonitorSettings)
|
foreach (var monitorSetting in profile.MonitorSettings)
|
||||||
{
|
{
|
||||||
var monitorItem = ViewModel.Monitors.FirstOrDefault(m => m.Monitor.InternalName == monitorSetting.MonitorInternalName);
|
var monitorItem = ViewModel.Monitors.FirstOrDefault(m => m.Monitor.InternalName == monitorSetting.MonitorId);
|
||||||
if (monitorItem != null)
|
if (monitorItem != null)
|
||||||
{
|
{
|
||||||
monitorItem.IsSelected = true;
|
monitorItem.IsSelected = true;
|
||||||
|
|||||||
@@ -89,12 +89,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
var settings = _monitors
|
var settings = _monitors
|
||||||
.Where(m => m.IsSelected)
|
.Where(m => m.IsSelected)
|
||||||
.Select(m => new ProfileMonitorSetting(
|
.Select(m => new ProfileMonitorSetting(
|
||||||
m.Monitor.HardwareId,
|
m.Monitor.InternalName, // Monitor Id (unique identifier)
|
||||||
m.IncludeBrightness ? (int?)m.Brightness : null,
|
m.IncludeBrightness ? (int?)m.Brightness : null,
|
||||||
m.IncludeColorTemperature && m.SupportsColorTemperature ? (int?)m.ColorTemperature : null,
|
m.IncludeColorTemperature && m.SupportsColorTemperature ? (int?)m.ColorTemperature : null,
|
||||||
m.IncludeContrast && m.SupportsContrast ? (int?)m.Contrast : null,
|
m.IncludeContrast && m.SupportsContrast ? (int?)m.Contrast : null,
|
||||||
m.IncludeVolume && m.SupportsVolume ? (int?)m.Volume : null,
|
m.IncludeVolume && m.SupportsVolume ? (int?)m.Volume : null))
|
||||||
m.Monitor.InternalName))
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return new PowerDisplayProfile(_profileName, settings);
|
return new PowerDisplayProfile(_profileName, settings);
|
||||||
|
|||||||
Reference in New Issue
Block a user