Improve monitor identification and display change handling

- Add detailed WMI monitor logging and fallback for missing names
- Update IdentifyWindow to support multi-monitor (mirrored) labels
- Map GDI device names to multiple monitor numbers for mirror mode
- Show pipe-separated monitor numbers in identify overlay
- Delay monitor refresh after display changes for hardware stability
- Enhance debug logging for monitor detection and mapping
This commit is contained in:
Yu Leng
2025-12-11 12:12:28 +08:00
parent 19eb78e696
commit f07fa4db60
3 changed files with 41 additions and 18 deletions

View File

@@ -277,7 +277,13 @@ namespace PowerDisplay.Common.Drivers.WMI
try
{
var instanceName = obj.GetPropertyValue<string>("InstanceName") ?? string.Empty;
var userFriendlyName = GetUserFriendlyName(obj) ?? "Internal Display";
var userFriendlyName = GetUserFriendlyName(obj);
Logger.LogDebug($"WMI MonitorID: InstanceName='{instanceName}', UserFriendlyName='{userFriendlyName ?? "(null)"}'");
if (string.IsNullOrEmpty(userFriendlyName))
{
userFriendlyName = "Internal Display";
}
if (!string.IsNullOrEmpty(instanceName))
{

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.UI;
@@ -30,10 +29,10 @@ namespace PowerDisplay.PowerDisplayXAML
[LibraryImport("user32.dll")]
private static partial uint GetDpiForWindow(IntPtr hwnd);
public IdentifyWindow(int number)
public IdentifyWindow(string displayText)
{
InitializeComponent();
NumberText.Text = number.ToString(CultureInfo.InvariantCulture);
NumberText.Text = displayText;
// Configure window style
ConfigureWindow();

View File

@@ -166,18 +166,25 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
try
{
// Get all display areas
// Get all display areas (virtual desktop regions)
var displayAreas = DisplayArea.FindAll();
Logger.LogDebug($"Found {displayAreas.Count} display areas");
// Get GDI device name to MonitorNumber mapping from QueryDisplayConfig
var displayInfoByGdiName = DdcCiNative.GetAllMonitorDisplayInfo()
.Values
.Where(info => info.MonitorNumber > 0)
.ToDictionary(info => info.GdiDeviceName, info => info.MonitorNumber, StringComparer.OrdinalIgnoreCase);
Logger.LogDebug($"Found {displayInfoByGdiName.Count} monitors with valid MonitorNumber from QueryDisplayConfig");
// Get all monitor info from QueryDisplayConfig
var allDisplayInfo = DdcCiNative.GetAllMonitorDisplayInfo().Values.ToList();
Logger.LogDebug($"Found {allDisplayInfo.Count} monitors from QueryDisplayConfig");
// For each DisplayArea, get its HMONITOR, then get GDI device name to find MonitorNumber
// Build GDI name to MonitorNumber(s) mapping
// Note: In mirror mode, multiple monitors may share the same GdiDeviceName
var gdiToMonitorNumbers = allDisplayInfo
.Where(info => info.MonitorNumber > 0)
.GroupBy(info => info.GdiDeviceName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(
g => g.Key,
g => g.Select(info => info.MonitorNumber).Distinct().OrderBy(n => n).ToList(),
StringComparer.OrdinalIgnoreCase);
// For each DisplayArea, get its HMONITOR, then get GDI device name to find MonitorNumber(s)
int windowsCreated = 0;
for (int i = 0; i < displayAreas.Count; i++)
{
@@ -201,17 +208,19 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
var gdiDeviceName = monitorInfo.GetDeviceName();
// Look up MonitorNumber by GDI device name
if (!displayInfoByGdiName.TryGetValue(gdiDeviceName, out int monitorNumber))
// Look up MonitorNumber(s) by GDI device name
if (!gdiToMonitorNumbers.TryGetValue(gdiDeviceName, out var monitorNumbers) || monitorNumbers.Count == 0)
{
Logger.LogDebug($"DisplayArea[{i}]: No MonitorNumber found for GDI device '{gdiDeviceName}'");
continue;
}
Logger.LogDebug($"DisplayArea[{i}]: GDI='{gdiDeviceName}' -> MonitorNumber={monitorNumber}");
// Format display text: single number for normal mode, "1|2" for mirror mode
var displayText = string.Join("|", monitorNumbers);
Logger.LogDebug($"DisplayArea[{i}]: GDI='{gdiDeviceName}' -> MonitorNumbers=[{displayText}]");
// Create and position identify window
var identifyWindow = new IdentifyWindow(monitorNumber);
var identifyWindow = new IdentifyWindow(displayText);
identifyWindow.PositionOnDisplay(displayArea);
identifyWindow.Activate();
windowsCreated++;
@@ -320,11 +329,20 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
/// <summary>
/// Handles display configuration changes detected by the DisplayChangeWatcher.
/// Triggers a monitor refresh to update the UI.
/// Triggers a monitor refresh to update the UI after a delay to allow hardware to stabilize.
/// </summary>
private async void OnDisplayChanged(object? sender, EventArgs e)
{
Logger.LogInfo("[MainViewModel] Display change detected, refreshing monitors...");
Logger.LogInfo("[MainViewModel] Display change detected, will refresh after 5 second delay...");
// Set scanning state immediately to provide visual feedback
IsScanning = true;
// Wait for hardware to stabilize (DDC/CI may not be ready immediately after plug)
await Task.Delay(TimeSpan.FromSeconds(5));
// Perform actual refresh
Logger.LogInfo("[MainViewModel] Delay complete, now refreshing monitors...");
await RefreshMonitorsAsync();
}