Skip to content

[Mouse Utilities] Add Auto Hide Cursor - #50271

Open
Damian Edwards (DamianEdwards) wants to merge 7 commits into
microsoft:mainfrom
DamianEdwards:damianedwards-auto-hide-cursor
Open

[Mouse Utilities] Add Auto Hide Cursor#50271
Damian Edwards (DamianEdwards) wants to merge 7 commits into
microsoft:mainfrom
DamianEdwards:damianedwards-auto-hide-cursor

Conversation

@DamianEdwards

@DamianEdwards Damian Edwards (DamianEdwards) commented Sep 1, 2026

Copy link
Copy Markdown
Member

Summary of the Pull Request

Fixes #11566

Adds a disabled-by-default Auto Hide Cursor utility under Mouse Utilities. Users can independently hide the pointer after keyboard input, after 1–60 seconds of pointer inactivity, or enable both triggers. Injected input and modifier-only key presses are ignored, and a small movement threshold filters pointer noise. Genuine pointer movement, mouse buttons, and wheel input restore the cursor.

The implementation runs low-level input hooks in a supervised Per-Monitor V2 worker. It temporarily replaces standard system cursor shapes and reloads the configured cursor scheme on disable, shutdown, or a worker/Runner failure. It does not use the ShowCursor counter or modify the registry cursor scheme.

Known limitation: applications that render fully custom or software cursors may remain visible.

PR Checklist

Detailed Description of the Pull Request / Additional comments

Settings, serialization, module enablement, GPO policy, telemetry, Quick Access metadata, Runner discovery, solution wiring, localization, and signing are included. If neither trigger is selected, the module installs no hooks and does not hide the cursor.

The module supervises the worker process and restores cursors if it exits unexpectedly. The worker watches the Runner and performs the same restoration if the Runner exits. This avoids leaving the cursor scheme replaced after a single-process failure.

Validation Steps Performed

  • Built x64 Debug Auto Hide Cursor module and worker.
  • Built x64 Debug GPOWrapper, Runner, and Settings UI.
  • Passed 7 native state-transition tests.
  • Passed 6 Settings serialization/default/bounds tests.
  • Verified live idle hide and restore against the displayed cursor.
  • Verified worker Per-Monitor V2 DPI awareness and distinct module/worker PDBs.
  • Verified the built Settings UI exposes Mouse Utilities > Auto Hide Cursor and completed a successful local user trial.

The validation host does not have Spectre-mitigated libraries installed, so local builds used /p:SpectreMitigation=false.

Screenshot

Auto Hide Cursor settings in Mouse Utilities

Add global hide-on-typing and idle-timeout behavior with supervised cursor restoration, Settings integration, policy support, telemetry, signing, and focused tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Thank you for contributing to PowerToys. We've detected that this PR might include a new or modified telemetry event. After this PR is merged, please follow these next steps:

@github-actions github-actions Bot added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something Product-Command Palette Refers to the Command Palette utility Product-Mouse Utilities Refers to the Mouse Utilities PowerToy labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

✅ PR intake

All automated intake checks now pass. Thanks for the updates!

Automated PR intake; PowerToys maintainers make final decisions.

@github-actions github-actions Bot added Ready for review and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Sep 1, 2026
@khmyznikov

Copy link
Copy Markdown
Contributor

Damian Edwards (@DamianEdwards) mouse utils now has actual UI Tests. Please make sure your new moduled covered by that as well. https://github.com/microsoft/PowerToys/tree/main/src/modules/MouseUtils/MouseUtils.UITests.Next You can use skills ui-tests-migration and ui-tests-local-vm to produce one.

Cover settings navigation, module lifecycle, independent triggers, worker behavior, idle-delay enablement, boundary values, and persistence.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@DamianEdwards

Copy link
Copy Markdown
Member Author

Damian Edwards (@DamianEdwards) mouse utils now has actual UI Tests. Please make sure your new moduled covered by that as well. https://github.com/microsoft/PowerToys/tree/main/src/modules/MouseUtils/MouseUtils.UITests.Next You can use skills ui-tests-migration and ui-tests-local-vm to produce one.

Added three MouseUtils.UITests.Next scenarios for Auto Hide Cursor in ede6dc652c:

  • navigation, section visibility, module enable/disable state, and worker start/stop
  • independent hide-on-typing/hide-on-idle toggles, including persistence across a Runner restart
  • idle-delay enabled state, 1/60-second boundary values, and persisted milliseconds

The tests include failure-safe cleanup that disables the module and reloads the system cursors. The x64 Debug UI-test project builds successfully and all three tests are discovered by Microsoft.Testing.Platform. Full VM execution is currently blocked because this host has no configured local UI-test VM root (X:\PowerToysUiTestVm) or host winapp.exe; I did not run the exclusive harness on the user's desktop.

@khmyznikov

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@khmyznikov

Copy link
Copy Markdown
Contributor

Damian Edwards (@DamianEdwards) check tests failures. https://dev.azure.com/shine-oss/PowerToys/_build/results?buildId=388022&view=results If you have a hyper-v on your machine, you can use the ui-tests-local-vm to setup the VM and do proper run there. It helps to get the tests CI ready.

Use the foreground-safe physical click path for the display-only SettingsExpander automation peer instead of unsupported UIA Invoke.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@DamianEdwards

Damian Edwards (DamianEdwards) commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Damian Edwards (@DamianEdwards) check tests failures. https://dev.azure.com/shine-oss/PowerToys/_build/results?buildId=388022&view=results If you have a hyper-v on your machine, you can use the ui-tests-local-vm to setup the VM and do proper run there. It helps to get the tests CI ready.

Diagnosed all six failures from build 388022: the two failing tests on x64Win10, x64Win11, and ARM64 all called winapp ui invoke on the SettingsExpander root, which is exposed as a display-only Group with no invoke pattern.

Fixed in 1e7bd8f3d8 by foregrounding Settings and using the .Next framework's real MouseClick path for ancestor-handled, non-invokable controls.

Validated the fix with the trusted local Hyper-V workflow on PowerToysUiTest-Win11 (Windows 11 Pro 10.0.26200, x64, standard user, 1920×1080). TestCategory=AutoHideCursor passed 3/3 with no failed, error, skipped, or not-executed results:

  • SectionNavigationAndModuleLifecycleAreAvailable — passed
  • TriggerTogglesPersistIndependentlyAndControlWorker — passed
  • IdleDelayEnabledStateAndBoundaryValuesPersist — passed

The x64 Debug product and test projects built successfully. The VM run used the repository-pinned winappcli v0.3.2 and a private .NET 10.0.11 desktop runtime. Durable result: localvm-20260903-014538-def932b4; test execution completed in 69.5 seconds with clean export evidence.

@DamianEdwards

Copy link
Copy Markdown
Member Author

Verified the Auto Hide Cursor UI tests locally with the repository's Hyper-V UI-test VM harness against PR head 1e7bd8f3d851b1af7776f354a4e899c9adb5fbba.

Guest: Windows 11 Pro 10.0.26200.0 x64, standard-user interactive desktop. Result: 3/3 passed, no failures:

  • SectionNavigationAndModuleLifecycleAreAvailable
  • TriggerTogglesPersistIndependentlyAndControlWorker
  • IdleDelayEnabledStateAndBoundaryValuesPersist

Evidence/run ID: localvm-20260903-014538-def932b4.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@khmyznikov

Copy link
Copy Markdown
Contributor

Damian Edwards (@DamianEdwards) one test remain flaky?

[+25/x0/?0] MouseUtils.UITests.Next.dll (net10.0|x64) - SpotlightModeUsesAlwaysColorAndRadius (4m 34s)

failed SpotlightModeUsesAlwaysColorAndRadius (8s 261ms)
  Assert.IsTrue failed. Calibrated relative movement ended at (1120,620), but overlay movement ended at (1130,625).
    at MouseUtils.UITests.MouseHighlighterTests.SpotlightModeUsesAlwaysColorAndRadius() in C:\a\_work\1\s\src\modules\MouseUtils\MouseUtils.UITests.Next\MouseHighlighterTests.cs:225
    at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
    at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
    
  Standard output
    Monitor '\\.\DISPLAY1': 1920x1080 at (0,0) primary=True
    Started screen recording at 15 FPS to D:\a\_work\1\TestResults\Deploy_ShineTest 20260903T044019_1684\In\UITestRecordings_32bab14b-e8bd-4559-817d-73e3fddc381b\recording_20260903_044443.mp4
    Video created: D:\a\_work\1\TestResults\Deploy_ShineTest 20260903T044019_1684\In\UITestRecordings_32bab14b-e8bd-4559-817d-73e3fddc381b\recording_20260903_044443.mp4 (0.2 MB)
    

Use absolute SendInput coordinates for Spotlight cursor tracking so the UI test still exercises the low-level mouse hook without depending on Windows pointer acceleration.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@DamianEdwards

Copy link
Copy Markdown
Member Author

Damian Edwards (@DamianEdwards) one test remain flaky?

I investigated this as a possible Auto Hide Cursor ordering/state leak. Before changing the test, the suspect Spotlight test passed 10/10 on this PR and 10/10 on clean origin/main from equivalent restored baselines. Running all three Auto Hide Cursor tests before Spotlight in one process passed 3/3 sequences (12/12 tests), and the full MouseUtils suite passed 43/43. After each sequence there were no PowerToys/AutoHideCursor/MouseHighlighter processes, no Auto Hide settings remained, the standard cursor was visible, all mouse buttons were up, and pointer-acceleration settings were unchanged.

The Windows 10 failure showed the real flaw: two nominally identical relative SendInput moves ended at (1120,620) and (1130,625), so the test depended on OS pointer acceleration/coalescing. Commit 902a7fae50532615e9f4d887a8d46551e03eb9ae now uses absolute SendInput: it still notifies Mouse Highlighter's low-level hook, but removes acceleration from calibration. With that fix, the PR passed 10/10, the identical fix against the clean main payload passed 10/10, the ordered sequence passed 12/12, and the final full suite passed 43/43 (localvm-20260903-182251-a523c5a7). These local A/B runs were Windows 11 Pro 10.0.26200.0 x64; the reported CI failure was x64 Windows 10, so that OS difference remains a local reproduction limitation.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

1 similar comment
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@khmyznikov

Copy link
Copy Markdown
Contributor

Damian Edwards (@DamianEdwards) still an error on Win10:
[+25/x0/?0] MouseUtils.UITests.Next.dll (net10.0|x64) - SpotlightModeUsesAlwaysColorAndRadius (4m 10s)

failed SpotlightModeUsesAlwaysColorAndRadius (7s 740ms)
Assert.IsTrue failed. Cursor moved to (960,540), expected (1120,620) within absolute-input normalization precision.
at MouseUtils.UITests.MouseHighlighterTests.SpotlightModeUsesAlwaysColorAndRadius() in C:\a_work\1\s\src\modules\MouseUtils\MouseUtils.UITests.Next\MouseHighlighterTests.cs:226
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

Standard output
Monitor '\.\DISPLAY1': 1920x1080 at (0,0) primary=True
Started screen recording at 15 FPS to D:\a_work\1\TestResults\Deploy_ShineTest 20260906T190830_11084\In\UITestRecordings_9bfa1cbd-82d5-4e0e-8436-9bde2d14852b\recording_20260906_191234.mp4
Video created: D:\a_work\1\TestResults\Deploy_ShineTest 20260906T190830_11084\In\UITestRecordings_9bfa1cbd-82d5-4e0e-8436-9bde2d14852b\recording_20260906_191234.mp4 (0.2 MB)

TestContext Messages:
Monitor '\\.\DISPLAY1': 1920x1080 at (0,0) primary=True

Remove the absolute SendInput helper that Windows 10 CI accepted without moving the cursor. Keep the proven relative-input hook path, capture the pre-activation desktop, and validate Spotlight at the actual accelerated endpoint.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@DamianEdwards

Copy link
Copy Markdown
Member Author

Damian Edwards (@DamianEdwards) still an error on Win10:

The absolute SendInput replacement was not cross-version-safe: build 388200 accepted the event but Windows 10 left the cursor at (960,540). Commit 1e42833cd0e5a46d59db6da508ba7ffc7d9f5deb removes that helper and restores the proven relative-input/low-level-hook path. The test now captures the desktop before activation, performs real relative movement, reads the actual accelerated endpoint, and verifies the configured Spotlight hole/tint at that endpoint—without assuming two injected motions land on identical coordinates.

Local Hyper-V validation on Windows 11 Pro 10.0.26200.0 x64: Spotlight 20/20 consecutive passes; five ordered Auto Hide Cursor → all Mouse Highlighter sequences passed 60/60 (localvm-20260907-182138-d0ceb768 through localvm-20260907-183107-9af4c3f5); full unfiltered suite passed 43/43 twice, including clean-baseline run localvm-20260907-184225-faaee819. No worker/process/button/cursor-state leak remained. This VM is Windows 11 only; the next x64 Win10 CI run is the cross-OS confirmation.

@DamianEdwards

Copy link
Copy Markdown
Member Author

Verified the full unfiltered MouseUtils.UITests.Next suite locally with the repository Hyper-V harness against PR head 1e42833cd0e5a46d59db6da508ba7ffc7d9f5deb on Windows 10 Pro 22H2 10.0.19045.6456 x64, standard-user interactive desktop. Result: 43/43 passed, with 0 failures, errors, skips, or not-executed tests, including SpotlightModeUsesAlwaysColorAndRadius. Run ID: localvm-20260907-225743-f152a53d.

Describe the module enablement event and its Enabled boolean in the diagnostic data inventory.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for two issues: the native module has an incorrect interface include that blocks compilation, and the worker can indefinitely postpone idle hiding while receiving ignored injected mouse input. Details and suggested fixes are inline. Findings are based on source inspection of commit c631c4d.

Comment thread src/modules/MouseUtils/AutoHideCursor/AutoHideCursorModule.cpp Outdated
Comment thread src/modules/MouseUtils/AutoHideCursor/AutoHideCursorWorker.cpp Outdated
Evaluate the physical idle deadline after every message wake and bound dispatch batches while retaining stop-handle priority. Cover repeated injected mouse input, a busy posted-message queue, and disable/restoration through the real worker in UI tests. Correct the module-interface relative include path.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Product-Command Palette Refers to the Command Palette utility Product-Mouse Utilities Refers to the Mouse Utilities PowerToy Ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto Hide mouse pointer/cursor

2 participants