From 2be4c4eb465490160516cb65dd1a0334c82c8ecd Mon Sep 17 00:00:00 2001 From: Thanh Nguyen <74597207+ThanhNguyxn@users.noreply.github.com> Date: Thu, 5 Feb 2026 06:58:49 -0500 Subject: [PATCH] Fix CursorWrap "Automatically activate on utility startup" setting not persisting (#45210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request Fixes #45185 - CursorWrap "Automatically activate on utility startup" setting cannot be disabled, and prevents spurious activation on startup. ## PR Checklist - [x] Closes: #45185 - [x] **Communication:** Issue was reported by community; fix follows established patterns from MousePointerCrosshairs - [x] **Tests:** Manual validation performed by contributor (video available) - [x] **Localization:** No new user-facing strings added - [ ] **Dev docs:** N/A - bug fix only - [ ] **New binaries:** N/A - no new binaries - [ ] **Documentation updated:** N/A - bug fix only ## Detailed Description of the Pull Request / Additional comments ### Problem Users reported that disabling the "Automatically activate on utility startup" setting for CursorWrap does not work - the mouse hook always starts automatically regardless of the setting value. ### Root Causes 1. **`dllmain.cpp` `enable()` method**: `StartMouseHook()` was always called unconditionally, ignoring `m_autoActivate`. 2. **`MouseUtilsViewModel.cs` `IsCursorWrapEnabled` setter**: enabling CursorWrap forced `AutoActivate = true`, overriding the user's preference. 3. **Startup edge case**: the trigger event could remain signaled from a previous session, immediately toggling CursorWrap on startup even when AutoActivate is off. ### Solution 1. **`src/modules/MouseUtils/CursorWrap/dllmain.cpp`**: only start the mouse hook if `m_autoActivate` is true. 2. **`src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs`**: remove the line that forced `AutoActivate = true` when enabling CursorWrap. 3. **`src/modules/MouseUtils/CursorWrap/dllmain.cpp`**: reset the trigger event on enable to avoid immediate activation on startup. ### Pattern Reference This fix follows the same pattern used by **MousePointerCrosshairs** module which has a similar `AutoActivate` setting that works correctly. ## Validation Steps Performed ### Build - `tools\build\build.ps1 -Platform x64 -Configuration Debug` ### Manual validation (contributor) #### Test Case 1: AutoActivate = false (should NOT auto-start mouse hook) 1. Open PowerToys Settings → Mouse Utilities → Cursor Wrap 2. Enable Cursor Wrap 3. **Disable** "Automatically activate on utility startup" 4. Close PowerToys completely (right-click tray icon → Exit) 5. Restart PowerToys 6. **Expected Result**: CursorWrap module is loaded but mouse hook is NOT active - cursor does NOT wrap at screen edges 7. Press activation hotkey (default: `Win+Alt+U`) 8. **Expected Result**: Mouse hook activates, cursor now wraps at screen edges 9. **Actual Result**: ✅ Works as expected #### Test Case 2: AutoActivate = true (should auto-start mouse hook) 1. Open PowerToys Settings → Mouse Utilities → Cursor Wrap 2. Enable Cursor Wrap 3. **Enable** "Automatically activate on utility startup" 4. Close PowerToys completely 5. Restart PowerToys 6. **Expected Result**: Mouse hook is immediately active, cursor wraps at screen edges without pressing hotkey 7. **Actual Result**: ✅ Works as expected #### Test Case 3: Setting persistence after restart 1. Set AutoActivate = false, restart PowerToys 2. Open Settings and verify AutoActivate is still false 3. Set AutoActivate = true, restart PowerToys 4. Open Settings and verify AutoActivate is still true 5. **Actual Result**: ✅ Setting persists correctly #### Test Case 4: Hotkey toggle works correctly 1. With AutoActivate = false, restart PowerToys 2. Press hotkey → cursor should start wrapping 3. Press hotkey again → cursor should stop wrapping 4. **Actual Result**: ✅ Hotkey toggle works correctly --- **Note**: Video demonstration available from contributor. --- src/modules/MouseUtils/CursorWrap/dllmain.cpp | 16 ++++++++++++++-- .../ViewModels/MouseUtilsViewModel.cs | 7 ------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/modules/MouseUtils/CursorWrap/dllmain.cpp b/src/modules/MouseUtils/CursorWrap/dllmain.cpp index add9fb7f92..b172f1c8b6 100644 --- a/src/modules/MouseUtils/CursorWrap/dllmain.cpp +++ b/src/modules/MouseUtils/CursorWrap/dllmain.cpp @@ -198,6 +198,10 @@ public: // Start listening for external trigger event so we can invoke the same logic as the activation hotkey. m_triggerEventHandle = CreateEventW(nullptr, false, false, CommonSharedConstants::CURSOR_WRAP_TRIGGER_EVENT); m_terminateEventHandle = CreateEventW(nullptr, false, false, nullptr); + if (m_triggerEventHandle) + { + ResetEvent(m_triggerEventHandle); + } if (m_triggerEventHandle && m_terminateEventHandle) { m_listening = true; @@ -212,8 +216,16 @@ public: // Create message window for display change notifications RegisterForDisplayChanges(); - StartMouseHook(); - Logger::info("CursorWrap enabled - mouse hook started"); + // Only start the mouse hook automatically if auto-activate is enabled + if (m_autoActivate) + { + StartMouseHook(); + Logger::info("CursorWrap enabled - mouse hook started (auto-activate on)"); + } + else + { + Logger::info("CursorWrap enabled - waiting for activation hotkey (auto-activate off)"); + } while (m_listening) { diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index bd6a431a20..678c090397 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -1006,13 +1006,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig.Enabled.CursorWrap = value; OnPropertyChanged(nameof(IsCursorWrapEnabled)); - // Auto-enable the AutoActivate setting when CursorWrap is enabled - // This ensures cursor wrapping is active immediately after enabling - if (value && !_cursorWrapAutoActivate) - { - CursorWrapAutoActivate = true; - } - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(outgoing.ToString());