From e213200b5a1a49e0d284e1cb054c7beeb9eca1cd Mon Sep 17 00:00:00 2001 From: moooyo <42196638+moooyo@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:58:36 +0800 Subject: [PATCH] [Color Picker] Handle default display refresh-rate sentinel values (#49973) ## Summary of the Pull Request Treats `dmDisplayFrequency` values `0` and `1` as the display hardware's default refresh rate instead of literal frequencies. Color Picker now retains its existing 60 Hz fallback for these sentinel values, preventing a timer interval overflow for `0` and one-second sampling for `1`. Valid refresh rates greater than `1` remain unchanged. ## PR Checklist - [x] Closes: #49971 - [ ] **Communication:** The issue has been filed for triage; this change has not yet been discussed with core contributors - [x] **Tests:** Added/updated and all pass - [x] **Localization:** No end-user-facing strings were added - [ ] **Dev docs:** Not applicable for this implementation-only bug fix - [ ] **New binaries:** No new binaries were added - [ ] **Documentation updated:** Not applicable; no user-facing documentation contract changed ## Detailed Description of the Pull Request / Additional comments `GetMainDisplayRefreshRate` now accepts a reported refresh rate only when it is greater than `1`. Otherwise, it keeps the existing 60 Hz fallback. A small test seam and unit tests cover reported values `0`, `1`, `60`, and `144`. The correct `InternalsVisibleTo` entry is added for the existing `ColorPickerUI.UnitTests` assembly. This issue was discovered while reviewing #49855, but it is an existing bug and this PR targets current `main` independently. Since #49855 also changes the refresh-rate code, whichever PR merges second may need a trivial rebase that preserves the `> 1` sentinel handling. No settings schema, IPC contract, dependencies, installer content, or production binaries were changed. ## Validation Steps Performed - `tools\build\build-essentials.cmd -Platform x64 -Configuration Debug`: passed with exit code 0, 0 warnings, and 0 errors. - `tools\build\build.cmd -Platform x64 -Configuration Debug` from `ColorPickerUI.UnitTests`: passed with exit code 0, 0 warnings, and 0 errors. - Full `vstest.console.exe` run for `ColorPickerUI.UnitTests.dll`: **382 passed, 0 failed**. - `git diff --check`: passed. Co-authored-by: Yu Leng (from Dev Box) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b3fb20d-6e9d-4fef-a5cd-f8921d28c220 --- .../Mouse/DisplayRefreshRateTest.cs | 23 +++++++++++++++++++ .../colorPicker/ColorPickerUI/Bootstrapper.cs | 1 + .../ColorPickerUI/Mouse/MouseInfoProvider.cs | 10 ++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/modules/colorPicker/ColorPickerUI.UnitTests/Mouse/DisplayRefreshRateTest.cs diff --git a/src/modules/colorPicker/ColorPickerUI.UnitTests/Mouse/DisplayRefreshRateTest.cs b/src/modules/colorPicker/ColorPickerUI.UnitTests/Mouse/DisplayRefreshRateTest.cs new file mode 100644 index 0000000000..58f6cc1237 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI.UnitTests/Mouse/DisplayRefreshRateTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using ColorPicker.Mouse; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ColorPicker.UnitTests.Mouse +{ + [TestClass] + public class DisplayRefreshRateTest + { + [TestMethod] + [DataRow(0, 60.0)] + [DataRow(1, 60.0)] + [DataRow(60, 60.0)] + [DataRow(144, 144.0)] + public void GetDisplayRefreshRateOrDefault_HandlesDefaultRefreshRateSentinels(int displayFrequency, double expectedRefreshRate) + { + Assert.AreEqual(expectedRefreshRate, MouseInfoProvider.GetDisplayRefreshRateOrDefault((uint)displayFrequency)); + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Bootstrapper.cs b/src/modules/colorPicker/ColorPickerUI/Bootstrapper.cs index 2aa4e273ad..c6c333a27b 100644 --- a/src/modules/colorPicker/ColorPickerUI/Bootstrapper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Bootstrapper.cs @@ -7,6 +7,7 @@ using System.ComponentModel.Composition.Hosting; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("UnitTest-ColorPickerUI")] +[assembly: InternalsVisibleTo("ColorPickerUI.UnitTests")] namespace ColorPicker { diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs index 4d6596bc3f..e58bc3bc58 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs @@ -21,6 +21,8 @@ namespace ColorPicker.Mouse [PartCreationPolicy(CreationPolicy.Shared)] public class MouseInfoProvider : IMouseInfoProvider { + private const double DefaultDisplayRefreshRate = 60.0; + private readonly double _mousePullInfoIntervalInMs; private readonly DispatcherTimer _timer = new DispatcherTimer(); private readonly MouseHook _mouseHook; @@ -123,13 +125,13 @@ namespace ColorPicker.Mouse private static double GetMainDisplayRefreshRate() { - double refreshRate = 60.0; + double refreshRate = DefaultDisplayRefreshRate; foreach (var monitor in MonitorResolutionHelper.AllMonitors) { if (monitor.IsPrimary && EnumDisplaySettingsW(monitor.Name, ENUM_CURRENT_SETTINGS, out DEVMODEW lpDevMode)) { - refreshRate = (double)lpDevMode.dmDisplayFrequency; + refreshRate = GetDisplayRefreshRateOrDefault(lpDevMode.dmDisplayFrequency); break; } } @@ -137,6 +139,10 @@ namespace ColorPicker.Mouse return refreshRate; } + // EnumDisplaySettings uses 0 and 1 to represent the hardware default refresh rate. + internal static double GetDisplayRefreshRateOrDefault(uint displayFrequency) + => displayFrequency > 1 ? displayFrequency : DefaultDisplayRefreshRate; + private void AppStateMonitor_AppClosed(object sender, EventArgs e) { DisposeHook();