-
Notifications
You must be signed in to change notification settings - Fork 7.6k
[MouseUtils] Implement MouseScrollRemap for horizontal scrolling (Fixes #44137) #44150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 13 commits
71bd2d2
9aad14b
d63147e
46efb5f
126e989
885f7c1
3134564
bdd1f6b
80e7c60
3ff5bc6
6963fc3
66c43bc
bfe69b3
5f9b57b
5b7a6e2
5de595d
280977a
e11e6db
64bfefd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,172 @@ | ||||||||||
| # MouseScrollRemap Implementation Summary | ||||||||||
|
|
||||||||||
| ## Overview | ||||||||||
| This implementation adds a new PowerToys utility called "MouseScrollRemap" that remaps `Shift + MouseWheel` to `Shift + Ctrl + MouseWheel` for consistent horizontal scrolling across all applications. | ||||||||||
|
|
||||||||||
| ## Problem Solved | ||||||||||
| Many applications (Chrome, JetBrains IDEs, GIMP) use `Shift + MouseWheel` for horizontal scrolling, while Microsoft Office applications use `Ctrl + Shift + MouseWheel`. This creates an inconsistent user experience. The MouseScrollRemap feature solves this by automatically converting `Shift + MouseWheel` to `Shift + Ctrl + MouseWheel` system-wide. | ||||||||||
|
|
||||||||||
| ## Implementation Details | ||||||||||
|
|
||||||||||
| ### Files Created | ||||||||||
| 1. **Module Core Files**: | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/dllmain.cpp` - Main implementation with mouse hook | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/pch.h/cpp` - Precompiled headers | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/trace.h/cpp` - ETW telemetry tracing | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/resource.h` - Resource definitions | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/MouseScrollRemap.rc` - Resource file | ||||||||||
|
|
||||||||||
| 2. **Build Configuration**: | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/MouseScrollRemap.vcxproj` - Visual Studio project file | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/MouseScrollRemap.vcxproj.filters` - Project filters | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/packages.config` - NuGet packages | ||||||||||
|
|
||||||||||
| 3. **Documentation**: | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/README.md` - Module documentation | ||||||||||
| - `src/modules/MouseUtils/MouseScrollRemap/SECURITY_REVIEW.md` - Security analysis | ||||||||||
| - `doc/devdocs/modules/mouseutils/readme.md` - Updated with new module info | ||||||||||
|
|
||||||||||
| ### Files Modified | ||||||||||
| 1. `src/common/logger/logger_settings.h` - Added mouseScrollRemapLoggerName | ||||||||||
| 2. `src/runner/main.cpp` - Added PowerToys.MouseScrollRemap.dll to module loading list | ||||||||||
| 3. `PowerToys.slnx` - Added project to solution | ||||||||||
|
|
||||||||||
| ### Technical Architecture | ||||||||||
|
|
||||||||||
| #### How It Works | ||||||||||
| 1. **Hook Installation**: When enabled, the module installs a low-level mouse hook (WH_MOUSE_LL) using SetWindowsHookEx | ||||||||||
| 2. **Event Detection**: The hook monitors WM_MOUSEWHEEL events | ||||||||||
| 3. **Modifier Key Check**: Uses GetAsyncKeyState to detect if Shift is pressed but Ctrl is not | ||||||||||
| 4. **Input Injection**: When Shift+MouseWheel is detected: | ||||||||||
| - Blocks the original event | ||||||||||
| - Injects Ctrl key down event | ||||||||||
| - Injects mouse wheel event with original mouseData | ||||||||||
| - Injects Ctrl key up event | ||||||||||
| - Result: Application receives Shift+Ctrl+MouseWheel | ||||||||||
|
|
||||||||||
| #### Key Components | ||||||||||
| - **PowertoyModuleIface**: Standard PowerToys module interface implementation | ||||||||||
| - **Low-level Mouse Hook**: WH_MOUSE_LL hook for intercepting mouse wheel events | ||||||||||
| - **SendInput API**: Used for keyboard and mouse event injection | ||||||||||
| - **Error Handling**: Validates SendInput success and falls back gracefully on failure | ||||||||||
| - **ETW Tracing**: Implements telemetry for enable/disable events | ||||||||||
|
|
||||||||||
| ## Building the Module | ||||||||||
|
|
||||||||||
| ### Prerequisites | ||||||||||
| - Visual Studio 2022 (version 17.4 or later) | ||||||||||
| - Windows 10 SDK (minimum version 1803) | ||||||||||
| - PowerToys repository cloned locally | ||||||||||
|
|
||||||||||
| ### Build Steps | ||||||||||
| 1. Open PowerToys.slnx in Visual Studio 2022 | ||||||||||
| 2. Set configuration to Debug or Release | ||||||||||
| 3. Set platform to x64 or ARM64 | ||||||||||
| 4. Build the solution or build just the MouseScrollRemap project | ||||||||||
|
|
||||||||||
| Alternatively, using command line: | ||||||||||
| ```powershell | ||||||||||
| cd /path/to/PowerToys | ||||||||||
| .\tools\build\build-essentials.ps1 | ||||||||||
| cd src\modules\MouseUtils\MouseScrollRemap | ||||||||||
| .\tools\build\build.ps1 | ||||||||||
|
Comment on lines
+71
to
+72
|
||||||||||
| cd src\modules\MouseUtils\MouseScrollRemap | |
| .\tools\build\build.ps1 | |
| # Build just the MouseScrollRemap project: | |
| .\tools\build\build.ps1 -projects src\modules\MouseUtils\MouseScrollRemap\MouseScrollRemap.vcxproj |
Fixed
Show fixed
Hide fixed
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| . |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #include <windows.h> | ||
| #include "resource.h" | ||
| #include "../../../common/version/version.h" | ||
|
|
||
| #define APSTUDIO_READONLY_SYMBOLS | ||
| #include "winres.h" | ||
| #undef APSTUDIO_READONLY_SYMBOLS | ||
|
|
||
| STRINGTABLE | ||
| BEGIN | ||
| IDS_MOUSESCROLLREMAP_NAME "Horizontal Scroll with Shift" | ||
Check failureCode scanning / check-spelling Unrecognized Spelling Error
MOUSESCROLLREMAP is not a recognized word. (unrecognized-spelling)
|
||
| END | ||
|
|
||
| 1 VERSIONINFO | ||
| FILEVERSION FILE_VERSION | ||
| PRODUCTVERSION PRODUCT_VERSION | ||
| FILEFLAGSMASK VS_FFI_FILEFLAGSMASK | ||
| #ifdef _DEBUG | ||
| FILEFLAGS VS_FF_DEBUG | ||
| #else | ||
| FILEFLAGS 0x0L | ||
| #endif | ||
| FILEOS VOS_NT_WINDOWS32 | ||
| FILETYPE VFT_DLL | ||
| FILESUBTYPE VFT2_UNKNOWN | ||
| BEGIN | ||
| BLOCK "StringFileInfo" | ||
| BEGIN | ||
| BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset | ||
| BEGIN | ||
| VALUE "CompanyName", COMPANY_NAME | ||
| VALUE "FileDescription", FILE_DESCRIPTION | ||
| VALUE "FileVersion", FILE_VERSION_STRING | ||
| VALUE "InternalName", INTERNAL_NAME | ||
| VALUE "LegalCopyright", COPYRIGHT_NOTE | ||
| VALUE "OriginalFilename", ORIGINAL_FILENAME | ||
| VALUE "ProductName", PRODUCT_NAME | ||
| VALUE "ProductVersion", PRODUCT_VERSION_STRING | ||
| END | ||
| END | ||
| BLOCK "VarFileInfo" | ||
| BEGIN | ||
| VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset | ||
| END | ||
| END | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation incorrectly states GetAsyncKeyState is used for modifier key detection. The implementation uses GetKeyState. Update documentation to match the actual implementation.