From e0010c56426af2722a02357e83c08a0ab2d489cb Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Fri, 7 Aug 2026 06:34:15 +0100 Subject: [PATCH] Ready for Review - [Mouse Jump] - port upstream WinUI3 code to Mouse Jump (microsoft#48290) (#48393) ## Summary of the Pull Request Changes for #48290 to convert Mouse Jump to a WinUI app and remove all remaining dependencies on WinForms, based on work already done in the original "FancyMouse" project (https://github.com/mikeclayton/FancyMouse). ## Notes for reviewers * the new WinUI build of the app is output into the "/WinUI3Apps" subfolder * there's 2 new assemblies that need to be added to the installation as well - MouseJump.HotKeys.dll and MouseJump.Models.dll. I'm not sure how to add those to the installer for signing / shipping... --- ### Summary of changes * New thumbnail layout and rendering code * WinUI rewrite (winforms version still committed) * MouseJump.Kicker (dev launch tool) * CsWin32 for interop * New assemblies - code reorganised --- ### 1. New layout code Incorporates latest FancyMouse core layout and rendering logic into Mouse Jump: * includes **support** for multiple devices in layout algorithms * preview still only shows local machine though * prerequisite for long-term goal #34126 image ### 2. WinUI rewrite Port latest stable FancyMouse WinUI implementation into Mouse Jump. Existing WinForms UI left in-situ side-by-side for now - easy to delete if not needed. ### 3. MouseJump.Kicker A small dev utility to start Mouse Jump without needing to build the runner project: image ### 4. Cswin32 for interop All win32 interop is now accessed via CsWin32 bindings. The original win32 bindings were heavily influenced by CsWin32 generated code (e.g. ```BOOL```, ```HWND```, etc structs), so there's not actually much change other than deleting a lot of boilerplate code. ### 5. New assemblies Some code has been reorganised into new assemblies to make it easier to keep Mouse Jump in sync with upstream FancyMouse * MouseJump.HotKeys * MouseJump.Models ## PR Checklist - [x] Closes #48290 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - all ui strings come from Resources.resw - [x] **Dev docs:** Added/updated - no changes required - [x] **New binaries:** Added on the required places - no changes required - [x] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [x] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [x] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [x] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [x] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx - no changes required ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed - **Workflow tests** - [x] Automated tests passing locally - [x] Minimal actions workflow (spelling check) passing for PR - [ ] Full actions workflow (msbuild) passing for PR - **UI tests** - [x] Happy path - [x] preview image appears when activated - [x] clicking the preview image moves the mouse cursor to the correct location - [x] right-click dismisses the preview image without moving the mouse - [x] pressing escape dismisses the preview image without moving the mouse - [x] left or right clicking another application / desktop dismisses the preview image without moving the mouse - [x] Works on multiple monitors with different dpi scaling settings (e.g. 100% vs 150%) * https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1411869418 * https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1412834413 - [x] Handling negative coordinates on non-primary monitors if higher or "lefter" than primary monitor * https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1404931694 - [x] Mouse crosshair moves when Mouse Jump moves the cursor (mouse clicks *and* keyboard shortcuts) * #24523 * #24527 - [x] Activating when the preview window is already visible moves the form to the new mouse position - [x] Number and key shortcuts (1-9, Home / End, Left / Right Arrow) jump to the appropriate monitor - [x] Number-pad shortcuts (1-9 jump to the appropriate monitor - **Settings tests** - [x] Changing thumbnail size settings updates the size of the thumbnail - [x] Changing preview type between Compact, Bezelled and Custom shows the correct preview type - [x] Changing custom preview settings shows the correct settings - [ ] Launching with settings version 1.0 upgrades settings to version 1.1, with "Bezelled" as the default style and the "Custom" settings preconfigured to match "Bezelled" - **Lifecycle tests** - [x] Starting PowerToys Runner launches MouseJump exe when enabled, and not when disabled - [x] Enabling / disabling Mouse Jump in settings starts / stops MouseJump exe - [x] Exiting PowerToys Runner stops MouseJump exe - [x] Killing runner exe via Task Manager stops MouseJump exe - [x] Stopping Visual Studio local debug run stops MouseJump exe - note - runner needs to be in *non*-admin mode otherwise Visual Studio debugger disconnects at launch - [x] Hotkey and size settings are automatically reloaded when config file is modified from Settings UI - [ ] ~~Hotkey and size settings are automatically reloaded when config file is modified manually (e.g. in notepad) while runner and MouseJumpUI.exe are running~~ - **[Internal Test Suite](https://github.com/microsoft/PowerToys/blob/5bc7201ae2b75b53d3a4bc35119c867ecf71c5f6/doc/releases/tests-checklist-template.md#mouse-utils)** - [x] Enable Mouse Jump. Then: - [x] Press the activation shortcut and verify the screens preview appears. - [x] Change activation shortcut and verify that new shortcut triggers Mouse Jump. - [x] Click around the screen preview and ensure that mouse cursor jumped to clicked location. - [x] Reorder screens in Display settings and confirm that Mouse Jump reflects the change and still works correctly. - [x] Change scaling of screens and confirm that Mouse Jump still works correctly. - [ ] Unplug additional monitors and confirm that Mouse Jump still works correctly. - [x] Disable Mouse Jump and verify that the module is not activated when you press the activation shortcut. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Boliang Zhang (from Dev Box) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: moooyo <42196638+moooyo@users.noreply.github.com> Co-authored-by: Yu Leng (from Dev Box) Copilot-Session: 8e04a72e-3b0f-4ac4-8156-d04ea9b8bb85 --- .github/actions/spell-check/excludes.txt | 3 +- .github/actions/spell-check/expect.txt | 45 +- PowerToys.slnx | 20 +- .../Helpers/DrawingHelperTests.cs | 190 +++- .../Helpers/LayoutHelperTests.cs | 621 ++++++---- .../Helpers/MouseHelperTests.cs | 6 +- .../Helpers/_test-win11-expected.png | Bin 207229 -> 206380 bytes .../MouseJump.Common.UnitTests.csproj | 70 +- .../{ConfigHelper.cs => ColorHelper.cs} | 5 +- .../MouseJump.Common/Helpers/DeviceHelper.cs | 37 + .../MouseJump.Common/Helpers/DpiModeHelper.cs | 55 + .../MouseJump.Common/Helpers/DrawingHelper.cs | 114 +- .../MouseJump.Common/Helpers/LayoutHelper.cs | 473 ++++++-- .../MouseJump.Common/Helpers/MouseHelper.cs | 119 +- .../MouseJump.Common/Helpers/ScreenHelper.cs | 72 +- .../MouseJump.Common/Helpers/StyleHelper.cs | 25 +- .../MouseJump.Common/Helpers/Win32Helper.cs | 111 ++ .../Imaging/DesktopImageRegionCopyService.cs | 57 +- .../Imaging/IImageRegionCopyService.cs | 6 +- .../Imaging/SolidColorRegionCopyService.cs | 47 + .../Imaging/StaticImageRegionCopyService.cs | 3 +- .../Interop/ResultHandler_int.cs | 121 ++ .../Interop/ResultHandler_nint.cs | 121 ++ .../Interop/ResultHandler_uint.cs | 54 + .../Interop/Win32SafeHandle.cs | 42 + .../MouseJump.Common/Interop/Win32Window.cs | 32 + .../Interop/Win32WindowClass.cs | 38 + .../Interop/Win32WindowProc.cs | 71 ++ .../Models/Drawing/BoxBounds.cs | 73 -- .../Models/Layout/PreviewLayout.cs | 131 --- .../MouseJump.Common/MouseJump.Common.csproj | 46 +- .../MouseJump.Common/NativeMethods.txt | 69 ++ .../NativeMethods/Core/CRECT.cs | 54 - .../NativeMethods/Core/HDC.cs | 39 - .../NativeMethods/Core/HMONITOR.cs | 47 - .../NativeMethods/Core/LONG.cs | 35 - .../NativeMethods/Core/LPCRECT.cs | 50 - .../NativeMethods/Core/LPPOINT.cs | 55 - .../NativeMethods/Core/LPRECT.cs | 50 - .../NativeMethods/Core/POINT.cs | 55 - .../NativeMethods/Core/RECT.cs | 54 - .../NativeMethods/Core/UINT.cs | 39 - .../NativeMethods/Core/ULONG_PTR.cs | 42 - .../NativeMethods/Core/WORD.cs | 35 - .../Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs | 37 - .../Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs | 26 - .../Graphics/Gdi/Gdi32.SetStretchBltMode.cs | 28 - .../Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs | 39 - .../Gdi/User32.EnumDisplayMonitors.cs | 34 - .../User32/Graphics/Gdi/User32.GetWindowDC.cs | 32 - .../Graphics/Gdi/User32.LPMONITORINFO.cs | 52 - .../Graphics/Gdi/User32.MONITORENUMPROC .cs | 22 - .../User32/Graphics/Gdi/User32.MONITORINFO.cs | 40 - .../Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs | 24 - .../Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs | 22 - .../Graphics/Gdi/User32.MonitorFromPoint .cs | 27 - .../User32/Graphics/Gdi/User32.ReleaseDC.cs | 29 - .../KeyboardAndMouse/User32.HARDWAREINPUT.cs | 39 - .../UI/Input/KeyboardAndMouse/User32.INPUT.cs | 50 - .../KeyboardAndMouse/User32.INPUT_TYPE.cs | 24 - .../KeyboardAndMouse/User32.KEYBDINPUT.cs | 44 - .../Input/KeyboardAndMouse/User32.LPINPUT.cs | 70 -- .../KeyboardAndMouse/User32.MOUSEINPUT.cs | 47 - .../User32.MOUSE_EVENT_FLAGS.cs | 33 - .../KeyboardAndMouse/User32.SendInput.cs | 29 - .../User32.GetCursorPos.cs | 26 - .../User32.GetDesktopWindow.cs | 25 - .../User32.GetSystemMetrics.cs | 26 - .../User32.SYSTEM_METRICS_INDEX.cs | 110 -- .../User32.SetCursorPos.cs | 29 - .../KeystrokeTests.cs | 61 + .../MouseJump.HotKeys.UnitTests.csproj | 26 + .../MouseJump.HotKeys/KeyModifiers.cs | 18 + .../MouseUtils/MouseJump.HotKeys/Keys.cs | 1005 +++++++++++++++++ .../MouseUtils/MouseJump.HotKeys/Keystroke.cs | 120 ++ .../MouseJump.HotKeys.csproj | 31 + .../MouseJump.HotKeys/NativeMethods.txt | 1 + .../Drawing/BoxBoundsTests.cs | 116 ++ .../Drawing/RectangleInfoTests.cs | 13 +- .../Drawing/SizeInfoTests.cs | 9 +- .../MSTestSettings.cs | 7 + .../MouseJump.Models.UnitTests.csproj | 26 + .../MouseJump.Models/Display/DesktopInfo.cs | 32 + .../MouseJump.Models/Display/DeviceInfo.cs | 42 + .../MouseJump.Models/Display/DisplayInfo.cs | 20 + .../MouseJump.Models/Display/HighDpiMode.cs | 17 + .../Display}/ScreenInfo.cs | 21 +- .../MouseJump.Models/Drawing/BoxBounds.cs | 167 +++ .../Drawing/PointInfo.cs | 38 +- .../Drawing/RectangleInfo.cs | 103 +- .../Drawing/SizeInfo.cs | 43 +- .../MouseJump.Models/MouseJump.Models.csproj | 20 + .../MouseJump.Models/NintJsonConverter.cs | 23 + .../Settings/PreviewType.cs | 2 +- .../Styles/BackgroundStyle.cs | 4 +- .../Styles/BorderStyle.cs | 9 +- .../Styles/BoxStyle.cs | 25 +- .../Styles/MarginStyle.cs | 2 +- .../Styles/PaddingStyle.cs | 2 +- .../Styles/PreviewStyle.cs | 16 +- .../ViewModel/CanvasViewModel.cs | 84 ++ .../ViewModel/DeviceViewModel.cs | 100 ++ .../ViewModel/FormViewModel.cs | 62 + .../ViewModel/ScreenViewModel.cs | 85 ++ .../Helpers/MouseJumpEventLoop.cs | 150 +++ .../Helpers/SettingsHelper.cs | 29 +- .../Helpers/ThrottledActionInvoker.cs | 2 +- .../MouseJump.WinUI3/MouseJump.WinUI3.csproj | 74 ++ .../MouseJump.WinUI3/MouseJumpXAML/App.xaml | 16 + .../MouseJumpXAML/App.xaml.cs | 112 ++ .../MouseJumpXAML/PreviewWindow.xaml | 20 + .../MouseJumpXAML/PreviewWindow.xaml.cs | 537 +++++++++ .../MouseJump.WinUI3/NativeMethods.txt | 14 + .../MouseUtils/MouseJump.WinUI3/Program.cs | 91 ++ .../Telemetry/MouseJumpShowEvent.cs | 13 +- .../Telemetry/MouseJumpTeleportCursorEvent.cs | 13 +- .../MouseUtils/MouseJump.WinUI3/app.manifest | 20 + .../MouseUtils/MouseJump/MouseJump.vcxproj | 6 +- src/modules/MouseUtils/MouseJump/dllmain.cpp | 58 +- .../MouseJumpUI/MainForm.Designer.cs | 94 -- .../MouseUtils/MouseJumpUI/MainForm.cs | 261 ----- .../MouseUtils/MouseJumpUI/MainForm.resx | 377 ------- .../MouseUtils/MouseJumpUI/MouseJumpUI.csproj | 40 - src/modules/MouseUtils/MouseJumpUI/Program.cs | 98 -- .../MouseUtils.UITests.csproj | 35 +- src/runner/main.cpp | 2 +- .../Settings.UI.Library/MouseJumpSettings.cs | 28 +- .../MouseJumpPreviewTypeConverter.cs | 2 +- .../Settings.UI/PowerToys.Settings.csproj | 3 +- .../Panels/MouseJumpPanel.xaml.cs | 26 +- .../MouseUtilsViewModel_MouseJump.cs | 95 +- .../Assets/Icons/MouseJump 16x16.ico | Bin 0 -> 1150 bytes tools/MouseJump.Kicker/KickerForm.Designer.cs | 96 ++ tools/MouseJump.Kicker/KickerForm.cs | 131 +++ tools/MouseJump.Kicker/KickerForm.resx | 145 +++ .../MouseJump.Kicker/MouseJump.Kicker.csproj | 20 + tools/MouseJump.Kicker/MouseJump.Kicker.slnx | 10 + .../NativeMethods/Core/BOOL.cs | 2 +- .../NativeMethods/Core/DWORD.cs | 8 +- .../NativeMethods/Core/HANDLE.cs | 4 +- .../NativeMethods/Core/HWND.cs | 13 +- .../NativeMethods/Core/LPARAM.cs | 18 +- .../NativeMethods/Core/LPCWSTR.cs | 56 + .../NativeMethods/Core/LPVOID.cs | 56 + .../Kernel32.LPSECURITY_ATTRIBUTES.cs | 56 + .../Security/Kernel32.SECURITY_ATTRIBUTES.cs | 34 + .../SystemServices/Kernel32.CreateEventW.cs | 32 + .../SystemServices/Kernel32.SetEvent.cs | 16 +- .../NativeMethods/Libraries.cs | 4 +- .../WindowsAndMessaging/User32.EnumWindows.cs | 38 + .../User32.GetWindowThreadProcessId.cs | 27 + .../User32.SetForegroundWindow.cs | 27 + tools/MouseJump.Kicker/Program.cs | 23 + tools/MouseJump.Kicker/readme.md | 11 + 154 files changed, 6325 insertions(+), 3464 deletions(-) rename src/modules/MouseUtils/MouseJump.Common/Helpers/{ConfigHelper.cs => ColorHelper.cs} (96%) create mode 100644 src/modules/MouseUtils/MouseJump.Common/Helpers/DeviceHelper.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Helpers/DpiModeHelper.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Helpers/Win32Helper.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Imaging/SolidColorRegionCopyService.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_int.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_nint.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_uint.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/Win32SafeHandle.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/Win32Window.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowClass.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowProc.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs create mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods.txt delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs delete mode 100644 src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/KeystrokeTests.cs create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/MouseJump.HotKeys.UnitTests.csproj create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys/KeyModifiers.cs create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys/Keys.cs create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys/Keystroke.cs create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys/MouseJump.HotKeys.csproj create mode 100644 src/modules/MouseUtils/MouseJump.HotKeys/NativeMethods.txt create mode 100644 src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/BoxBoundsTests.cs rename src/modules/MouseUtils/{MouseJump.Common.UnitTests/Models => MouseJump.Models.UnitTests}/Drawing/RectangleInfoTests.cs (93%) rename src/modules/MouseUtils/{MouseJump.Common.UnitTests/Models => MouseJump.Models.UnitTests}/Drawing/SizeInfoTests.cs (93%) create mode 100644 src/modules/MouseUtils/MouseJump.Models.UnitTests/MSTestSettings.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models.UnitTests/MouseJump.Models.UnitTests.csproj create mode 100644 src/modules/MouseUtils/MouseJump.Models/Display/DesktopInfo.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/Display/DeviceInfo.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/Display/DisplayInfo.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/Display/HighDpiMode.cs rename src/modules/MouseUtils/{MouseJump.Common/Models/Drawing => MouseJump.Models/Display}/ScreenInfo.cs (61%) create mode 100644 src/modules/MouseUtils/MouseJump.Models/Drawing/BoxBounds.cs rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Drawing/PointInfo.cs (78%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Drawing/RectangleInfo.cs (87%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Drawing/SizeInfo.cs (86%) create mode 100644 src/modules/MouseUtils/MouseJump.Models/MouseJump.Models.csproj create mode 100644 src/modules/MouseUtils/MouseJump.Models/NintJsonConverter.cs rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Settings/PreviewType.cs (85%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/BackgroundStyle.cs (93%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/BorderStyle.cs (88%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/BoxStyle.cs (82%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/MarginStyle.cs (96%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/PaddingStyle.cs (96%) rename src/modules/MouseUtils/{MouseJump.Common/Models => MouseJump.Models}/Styles/PreviewStyle.cs (66%) create mode 100644 src/modules/MouseUtils/MouseJump.Models/ViewModel/CanvasViewModel.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/ViewModel/DeviceViewModel.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/ViewModel/FormViewModel.cs create mode 100644 src/modules/MouseUtils/MouseJump.Models/ViewModel/ScreenViewModel.cs create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/Helpers/MouseJumpEventLoop.cs rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.WinUI3}/Helpers/SettingsHelper.cs (88%) rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.WinUI3}/Helpers/ThrottledActionInvoker.cs (96%) create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/MouseJump.WinUI3.csproj create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml.cs create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml.cs create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/NativeMethods.txt create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/Program.cs rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.WinUI3}/Telemetry/MouseJumpShowEvent.cs (54%) rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.WinUI3}/Telemetry/MouseJumpTeleportCursorEvent.cs (53%) create mode 100644 src/modules/MouseUtils/MouseJump.WinUI3/app.manifest delete mode 100644 src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/MainForm.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/MainForm.resx delete mode 100644 src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj delete mode 100644 src/modules/MouseUtils/MouseJumpUI/Program.cs create mode 100644 tools/MouseJump.Kicker/Assets/Icons/MouseJump 16x16.ico create mode 100644 tools/MouseJump.Kicker/KickerForm.Designer.cs create mode 100644 tools/MouseJump.Kicker/KickerForm.cs create mode 100644 tools/MouseJump.Kicker/KickerForm.resx create mode 100644 tools/MouseJump.Kicker/MouseJump.Kicker.csproj create mode 100644 tools/MouseJump.Kicker/MouseJump.Kicker.slnx rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Core/BOOL.cs (96%) rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Core/DWORD.cs (81%) rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Core/HANDLE.cs (94%) rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Core/HWND.cs (71%) rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Core/LPARAM.cs (58%) create mode 100644 tools/MouseJump.Kicker/NativeMethods/Core/LPCWSTR.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/Core/LPVOID.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.LPSECURITY_ATTRIBUTES.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.SECURITY_ATTRIBUTES.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.CreateEventW.cs rename src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs => tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.SetEvent.cs (59%) rename {src/modules/MouseUtils/MouseJump.Common => tools/MouseJump.Kicker}/NativeMethods/Libraries.cs (69%) create mode 100644 tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.EnumWindows.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.GetWindowThreadProcessId.cs create mode 100644 tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.SetForegroundWindow.cs create mode 100644 tools/MouseJump.Kicker/Program.cs create mode 100644 tools/MouseJump.Kicker/readme.md diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 24045c080a..e9fb1cadf8 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -114,7 +114,7 @@ ^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/Text/.*\.cs$ ^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$ ^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/ -^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$ +^src/modules/MouseUtils/MouseJumpUI/UI/MainForm\.resx$ ^src/modules/MouseWithoutBorders/App/.*/NativeMethods\.cs$ ^src/modules/MouseWithoutBorders/App/Form/.*\.Designer\.cs$ ^src/modules/MouseWithoutBorders/App/Form/.*\.resx$ @@ -139,6 +139,7 @@ ^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$ ^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$ ^src/Monaco/ +^tools/MouseJump.Kicker/KickerForm\.resx$ ^tools/project_template/ModuleTemplate/resource\.h$ ^tools/Verification scripts/Check preview handler registration\.ps1$ ignore$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 27d8cbcf94..587e2d59de 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -47,7 +47,6 @@ ALLVIEW ALPHATYPE altkey amr -ANDSCANS animatedvisuals Animnate AOC @@ -194,6 +193,7 @@ CAPTURECHANGED CARETBLINKING carlos Carlseibert +cascadia caseinsensitive caub CBN @@ -313,7 +313,6 @@ CREATESCHEDULEDTASK CREATESTRUCT CREATEWINDOWFAILED creativecommons -CRECT CRH critsec cropandlock @@ -389,7 +388,6 @@ DELA DELD deld DELETEDKEYIMAGE -DELETESCANS DEMOTYPE DENORMAL depersist @@ -457,10 +455,8 @@ dreamsofameaningfullife drivedetectionwarning DROPFILES DSPDLOG -DSTINVERT DString dto -DUMMYUNIONNAME dumpbin dutil DVASPECT @@ -505,6 +501,8 @@ encodedlaunch encryptor ENDSESSION ENSUREVISIBLE +ENTERIDLE +ENTERMENULOOP ENTERSIZEMOVE ENTRYW ENU @@ -538,6 +536,8 @@ EXECUTEDEFAULT executionpolicy exename exf +EXITCOMMAND +EXITMENULOOP EXITSIZEMOVE exlist EXPCMDFLAGS @@ -669,6 +669,7 @@ gwl GWLP GWLSTYLE hangeul +Hanguel Hantai Hanzi Hardlines @@ -812,6 +813,7 @@ imageresizerinput imageresizersettings imagingdevices ime +IMEAceept imgflip inapp inbox @@ -826,6 +828,8 @@ INITCOMMONCONTROLSEX INITDIALOG INITGUID initialfile +INITMENU +INITMENUPOPUP INITTOLOGFONTSTRUCT inkscape INLINEPREFIX @@ -930,6 +934,7 @@ Lclean Ldone Ldr LEFTALIGN +LEFTBUTTON leftclick LEFTSCROLLBAR LEFTTEXT @@ -981,11 +986,9 @@ LPCLSID lpcmi LPCMINVOKECOMMANDINFO LPCREATESTRUCT -LPCRECT LPCTSTR lpdw lpfn -LPINPUT LPITEMIDLIST lpmi LPMINMAXINFO @@ -994,6 +997,7 @@ LPOSVERSIONINFOEXW LPQUERY lprc LPSAFEARRAY +LPSECURITY lpstr lpsz LPTHREAD @@ -1054,8 +1058,7 @@ meme memicmp MENUITEMINFO MENUITEMINFOW -MERGECOPY -MERGEPAINT +MENUSELECT Metacharacter metadatamatters Metadatas @@ -1202,7 +1205,6 @@ NOASYNC NOCHANGEDIR NOCLIP NOCLOSEPROCESS -NOCOALESCE NOCOMM NOCONFIRMMKDIR NOCOPYBITS @@ -1219,10 +1221,10 @@ NOLINKINFO nologo NOMCX NOMINMAX -NOMIRRORBITMAP nomove NONANTIALIASED nonclient +Nonconvert NONCLIENTMETRICSW NONELEVATED nonspace @@ -1251,8 +1253,6 @@ NOTIFYICONDATAW NOTIMPL NOTOPMOST NOTRACK -NOTSRCCOPY -NOTSRCERASE Notupdated notwindows NOTXORPEN @@ -1277,7 +1277,10 @@ nwc ocr Ocrsettings odbccp +Oemcomma OEMCONVERT +Oemplus +Oemtilde officehubintl OFN ofs @@ -1302,7 +1305,6 @@ openxmlformats OPTIMIZEFORINVOKE Optronics ORPHANEDDIALOGTITLE -ORSCANS oss OSVERSIONINFO OSVERSIONINFOEX @@ -1334,10 +1336,7 @@ PARENTRELATIVEPARSING parray parseable PARTIALCONFIRMATIONDIALOGTITLE -PATCOPY PATHMUSTEXIST -PATINVERT -PATPAINT pbc pbi PBP @@ -1567,6 +1566,7 @@ REMAPSUCCESSFUL REMAPUNSUCCESSFUL Remotable remotedesktop +remotehost remoteip Removelnk renamable @@ -1603,7 +1603,6 @@ RIGHTBUTTON RIGHTSCROLLBAR riid rollups -rop ROUNDSMALL rpcrt RRF @@ -1717,6 +1716,7 @@ shortdate SHORTPATH shortsplit showcolorname +SHOWCONTEXTMENU SHOWDEFAULT SHOWELEVATIONPROMPT SHOWMAGNIFIEDCURSOR @@ -1779,12 +1779,8 @@ spsi spsia spsrm spsv -SRCAND SRCCOPY -SRCERASE Srch -SRCINVERT -SRCPAINT SResize srme srre @@ -1948,6 +1944,7 @@ tracerpt trackbar trafficmanager transicc +TRAYICON TRANSIENTWINDOW transitioning TRAYMOUSEMESSAGE @@ -1966,6 +1963,8 @@ TYPEMOUSE TYPESHORTCUT UAC UACUI +UAHINITMENU +UAHMEASUREMENUITEM UAL uap UBR @@ -1990,6 +1989,7 @@ unescaped unforgeable ungroup UNICODETEXT +UNINITMENUPOPUP unins uninsdeletekey Uninstaller @@ -2060,7 +2060,6 @@ vgetq viewmodels virama VIRTKEY -VIRTUALDESK VISEGRADRELAY visiblecolorformats visio diff --git a/PowerToys.slnx b/PowerToys.slnx index 645a61167d..e8fa38ffb7 100644 --- a/PowerToys.slnx +++ b/PowerToys.slnx @@ -545,11 +545,19 @@ - - + + + + + + + + + + @@ -557,6 +565,14 @@ + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs index 63f372f20b..ad9379326e 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs @@ -2,29 +2,32 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Globalization; +using System.Drawing; +using System.Drawing.Imaging; using System.Reflection; -using System.Text; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using MouseJump.Common.Helpers; using MouseJump.Common.Imaging; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Styles; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; namespace MouseJump.Common.UnitTests.Helpers; -[TestClass] public static class DrawingHelperTests { [TestClass] - public sealed class RenderPreviewTests + public sealed class GetPreviewLayoutTests { public sealed class TestCase { - public TestCase(PreviewStyle previewStyle, List screens, PointInfo activatedLocation, string desktopImageFilename, string expectedImageFilename) + public TestCase(PreviewStyle previewStyle, DisplayInfo displayInfo, ScreenInfo activatedScreen, PointInfo activatedLocation, string desktopImageFilename, string expectedImageFilename) { this.PreviewStyle = previewStyle; - this.Screens = screens; + this.DisplayInfo = displayInfo; + this.ActivatedScreen = activatedScreen; this.ActivatedLocation = activatedLocation; this.DesktopImageFilename = desktopImageFilename; this.ExpectedImageFilename = expectedImageFilename; @@ -32,7 +35,9 @@ public static class DrawingHelperTests public PreviewStyle PreviewStyle { get; } - public List Screens { get; } + public DisplayInfo DisplayInfo { get; } + + public ScreenInfo ActivatedScreen { get; } public PointInfo ActivatedLocation { get; } @@ -43,32 +48,94 @@ public static class DrawingHelperTests public static IEnumerable GetTestCases() { - /* 4-grid */ + // colors like "SystemColors.Highlight" can change between versions of windows, + // so we'll use hard-coded colors instead to reduce false test failures + var systemHighlight = Color.FromArgb(0, 120, 215); + var previewStyle = StyleHelper.BezelledPreviewStyle; + previewStyle = new( + canvasSize: previewStyle.CanvasSize, + canvasStyle: new( + marginStyle: previewStyle.CanvasStyle.MarginStyle, + borderStyle: previewStyle.CanvasStyle.BorderStyle.WithColor(systemHighlight), + paddingStyle: previewStyle.CanvasStyle.PaddingStyle, + backgroundStyle: previewStyle.CanvasStyle.BackgroundStyle + ), + screenStyle: previewStyle.ScreenStyle, + extraColors: previewStyle.ExtraColors + ); + + // 4-grid + var displayInfo = new DisplayInfo( + devices: new DeviceInfo[] + { + new( + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 500, 500), + workingArea: new(0, 0, 500, 500)), + new( + handle: 0, + primary: false, + displayArea: new(500, 0, 500, 500), + workingArea: new(500, 0, 500, 500)), + new( + handle: 0, + primary: false, + displayArea: new(500, 500, 500, 500), + workingArea: new(500, 500, 500, 500)), + new( + handle: 0, + primary: false, + displayArea: new(0, 500, 500, 500), + workingArea: new(0, 500, 500, 500)), + } + ), + }); yield return new object[] { new TestCase( - previewStyle: StyleHelper.BezelledPreviewStyle, - screens: new List() - { - new(0, 0, 500, 500), - new(500, 0, 500, 500), - new(500, 500, 500, 500), - new(0, 500, 500, 500), - }, + previewStyle: previewStyle, + displayInfo: displayInfo, + activatedScreen: displayInfo.Devices[0].Screens[0], activatedLocation: new(x: 50, y: 50), desktopImageFilename: "_test-4grid-desktop.png", expectedImageFilename: "_test-4grid-expected.png"), }; - /* win 11 */ + + // win 11 + displayInfo = new( + devices: new DeviceInfo[] + { + new( + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(5120, 349, 1920, 1080), + workingArea: new(5120, 349, 1920, 1080)), + new( + handle: 0, + primary: false, + displayArea: new(0, 0, 5120, 1440), + workingArea: new(0, 0, 5120, 1440)), + } + ), + } + ); yield return new object[] { new TestCase( - previewStyle: StyleHelper.BezelledPreviewStyle, - screens: new List() - { - new(5120, 349, 1920, 1080), - new(0, 0, 5120, 1440), - }, + previewStyle: previewStyle, + displayInfo: displayInfo, + activatedScreen: displayInfo.Devices[0].Screens[0], activatedLocation: new(x: 50, y: 50), desktopImageFilename: "_test-win11-desktop.png", expectedImageFilename: "_test-win11-expected.png"), @@ -76,22 +143,36 @@ public static class DrawingHelperTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) + [DynamicData(nameof(GetTestCases))] + public async Task RunTestCases(TestCase data) { // load the fake desktop image - using var desktopImage = RenderPreviewTests.LoadImageResource(data.DesktopImageFilename); + using var desktopImage = GetPreviewLayoutTests.LoadImageResource(data.DesktopImageFilename); + + var formLayout = LayoutHelper.GetFormLayout( + previewStyle: data.PreviewStyle, + displayInfo: data.DisplayInfo, + activatedScreen: data.ActivatedScreen, + activatedLocation: data.ActivatedLocation); + + var imageCopyServices = formLayout.CanvasLayout.DeviceLayouts + .Select( + deviceLayout => (IImageRegionCopyService)new StaticImageRegionCopyService(desktopImage)) + .ToList(); // draw the preview image - var previewLayout = LayoutHelper.GetPreviewLayout( - previewStyle: data.PreviewStyle, - screens: data.Screens, - activatedLocation: data.ActivatedLocation); - var imageCopyService = new StaticImageRegionCopyService(desktopImage); - using var actual = DrawingHelper.RenderPreview(previewLayout, imageCopyService); + using var actual = await DrawingHelper.RenderPreviewAsync(formLayout.CanvasLayout, data.ActivatedScreen, imageCopyServices); + + // save the actual image so we can pick it up as a build artifact + var actualFilename = Path.GetFileNameWithoutExtension(data.ExpectedImageFilename) + "_actual" + Path.GetExtension(data.ExpectedImageFilename); + actual.Save(actualFilename, ImageFormat.Png); // load the expected image - var expected = RenderPreviewTests.LoadImageResource(data.ExpectedImageFilename); + var expected = GetPreviewLayoutTests.LoadImageResource(data.ExpectedImageFilename); + + // save the actual image so we can pick it up as a build artifact + var expectedFilename = Path.GetFileNameWithoutExtension(data.ExpectedImageFilename) + "_expected" + Path.GetExtension(data.ExpectedImageFilename); + expected.Save(expectedFilename, ImageFormat.Png); // compare the images AssertImagesEqual(expected, actual); @@ -99,24 +180,26 @@ public static class DrawingHelperTests private static Bitmap LoadImageResource(string filename) { - var assembly = Assembly.GetExecutingAssembly(); - var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException()); - var resourceName = $"{typeof(DrawingHelperTests).Namespace}.{filename.Replace("/", ".")}"; - var resourceNames = assembly.GetManifestResourceNames(); + var referenceType = typeof(DrawingHelperTests); + var resourceAssembly = referenceType.Assembly; + + var resourcePrefix = referenceType.Namespace; + var resourceName = $"{resourcePrefix}.{filename.Replace("/", ".")}"; + var resourceNames = resourceAssembly.GetManifestResourceNames(); if (!resourceNames.Contains(resourceName)) { - var message = new StringBuilder(); - message.AppendLine(CultureInfo.InvariantCulture, $"Embedded resource '{resourceName}' does not exist."); - message.AppendLine($"Known resources:"); - foreach (var name in resourceNames) - { - message.AppendLine(name); - } - - throw new InvalidOperationException(message.ToString()); + var message = string.Join( + Environment.NewLine, + new List([ + $"Embedded resource '{resourceName}' does not exist.", + string.Empty, + "Valid resource names are:", + string.Empty]) + .Concat(resourceNames)); + throw new InvalidOperationException(message); } - var stream = assembly.GetManifestResourceStream(resourceName) + var stream = resourceAssembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException(); var image = (Bitmap)Image.FromStream(stream); return image; @@ -143,12 +226,11 @@ public static class DrawingHelperTests var actualPixel = actual.GetPixel(x, y); // allow a small tolerance for rounding differences in gdi - // using a tolerance of 3 for support of minor differences in Windows Server 2025 CI Assert.IsTrue( - (Math.Abs(expectedPixel.A - actualPixel.A) <= 3) && - (Math.Abs(expectedPixel.R - actualPixel.R) <= 3) && - (Math.Abs(expectedPixel.G - actualPixel.G) <= 3) && - (Math.Abs(expectedPixel.B - actualPixel.B) <= 3), + (Math.Abs(expectedPixel.A - actualPixel.A) <= 1) && + (Math.Abs(expectedPixel.R - actualPixel.R) <= 1) && + (Math.Abs(expectedPixel.G - actualPixel.G) <= 1) && + (Math.Abs(expectedPixel.B - actualPixel.B) <= 1), $"images differ at pixel ({x}, {y}) - expected: {expectedPixel}, actual: {actualPixel}"); } } diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs index 95d364b100..8e7804e20e 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs @@ -2,17 +2,20 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; +using System.Drawing; using System.Text.Json; using Microsoft.VisualStudio.TestTools.UnitTesting; + using MouseJump.Common.Helpers; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Layout; -using MouseJump.Common.Models.Styles; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; +using MouseJump.Models.ViewModel; namespace MouseJump.Common.UnitTests.Helpers; -[TestClass] public static class LayoutHelperTests { /* @@ -109,26 +112,32 @@ public static class LayoutHelperTests { public sealed class TestCase { - public TestCase(PreviewStyle previewStyle, List screens, PointInfo activatedLocation, PreviewLayout expectedResult) + public TestCase(string testName, PreviewStyle previewStyle, DisplayInfo displayInfo, ScreenInfo activatedScreen, PointInfo activatedLocation, FormViewModel expectedResult) { + this.TestName = testName; this.PreviewStyle = previewStyle; - this.Screens = screens; + this.DisplayInfo = displayInfo; this.ActivatedLocation = activatedLocation; + this.ActivatedScreen = activatedScreen; this.ExpectedResult = expectedResult; } + public string TestName { get; } + public PreviewStyle PreviewStyle { get; } - public List Screens { get; } + public DisplayInfo DisplayInfo { get; } + + public ScreenInfo ActivatedScreen { get; } public PointInfo ActivatedLocation { get; } - public PreviewLayout ExpectedResult { get; } + public FormViewModel ExpectedResult { get; } } public static IEnumerable GetTestCases() { - // happy path - single screen with 50% scaling, + // happy path - single device with screen and 50% scaling, // *has* a preview border but *no* screenshot borders // // +----------------+ @@ -136,10 +145,11 @@ public static class LayoutHelperTests // | 0 | // | | // +----------------+ + var testName = "Test 1 - Happy Path"; var previewStyle = new PreviewStyle( canvasSize: new( - width: 524, - height: 396 + width: (1024 / 2) + (5 * 2) + (1 * 2), // half the screen size, plus additional room for canvas border and padding + height: (768 / 2) + (5 * 2) + (1 * 2) // half the screen size, plus additional room for canvas border and padding ), canvasStyle: new( marginStyle: MarginStyle.Empty, @@ -154,38 +164,56 @@ public static class LayoutHelperTests color2: Color.FromArgb(3, 68, 192) // darker blue ) ), - screenStyle: BoxStyle.Empty); - var screens = new List - { - new(0, 0, 1024, 768), - }; - var activatedLocation = new PointInfo(512, 384); - var expectedResult = new PreviewLayout( - virtualScreen: new(0, 0, 1024, 768), - screens: screens, - activatedScreenIndex: 0, - formBounds: new(250, 186, 524, 396), - previewStyle: previewStyle, - previewBounds: new( - outerBounds: new(0, 0, 524, 396), - marginBounds: new(0, 0, 524, 396), - borderBounds: new(0, 0, 524, 396), - paddingBounds: new(5, 5, 514, 386), - contentBounds: new(6, 6, 512, 384) - ), - screenshotBounds: new() + screenStyle: BoxStyle.Empty, + extraColors: Array.Empty()); + var displayInfo = new DisplayInfo( + devices: new List { new( - outerBounds: new(6, 6, 512, 384), - marginBounds: new(6, 6, 512, 384), - borderBounds: new(6, 6, 512, 384), - paddingBounds: new(6, 6, 512, 384), - contentBounds: new(6, 6, 512, 384) + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 1024, 768), + workingArea: new(0, 0, 1024, 768)), + } ), }); - yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) }; + var activatedScreen = displayInfo.Devices[0].Screens[0]; + var activatedLocation = activatedScreen.DisplayArea.Midpoint; + var expectedResult = new FormViewModel( + formBounds: new(250, 186, 524, 396), + canvasLayout: new( + canvasBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 524, 396), + boxStyle: previewStyle.CanvasStyle), + canvasStyle: previewStyle.CanvasStyle, + deviceLayouts: new List() + { + new( + deviceInfo: displayInfo.Devices[0], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(6, 6, 512, 384), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List + { + new( + screenInfo: displayInfo.Devices[0].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(6, 6, 512, 384), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + } + )); + yield return new object[] { new TestCase(testName, previewStyle, displayInfo, activatedScreen, activatedLocation, expectedResult) }; - // happy path - single screen with 50% scaling, + // happy path - single device with screen and 50% scaling, // *no* preview borders but *has* screenshot borders // // +----------------+ @@ -193,10 +221,11 @@ public static class LayoutHelperTests // | 0 | // | | // +----------------+ + testName = "Test 2 - Happy Path"; previewStyle = new PreviewStyle( canvasSize: new( - width: 512, - height: 384 + width: 1024 / 2, // half the screen size + height: 768 / 2 // half the screen size ), canvasStyle: BoxStyle.Empty, screenStyle: new( @@ -210,37 +239,54 @@ public static class LayoutHelperTests backgroundStyle: new( color1: Color.FromArgb(13, 87, 210), // light blue color2: Color.FromArgb(3, 68, 192) // darker blue - ) - )); - screens = new List - { - new(0, 0, 1024, 768), - }; - activatedLocation = new PointInfo(512, 384); - expectedResult = new PreviewLayout( - virtualScreen: new(0, 0, 1024, 768), - screens: screens, - activatedScreenIndex: 0, - formBounds: new(256, 192, 512, 384), - previewStyle: previewStyle, - previewBounds: new( - outerBounds: new(0, 0, 512, 384), - marginBounds: new(0, 0, 512, 384), - borderBounds: new(0, 0, 512, 384), - paddingBounds: new(0, 0, 512, 384), - contentBounds: new(0, 0, 512, 384) - ), - screenshotBounds: new() + )), + extraColors: Array.Empty()); + displayInfo = new DisplayInfo( + devices: new List { new( - outerBounds: new(0, 0, 512, 384), - marginBounds: new(0, 0, 512, 384), - borderBounds: new(1, 1, 510, 382), - paddingBounds: new(6, 6, 500, 372), - contentBounds: new(6, 6, 500, 372) + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 1024, 768), + workingArea: new(0, 0, 1024, 768)), + } ), }); - yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) }; + activatedScreen = displayInfo.Devices[0].Screens[0]; + activatedLocation = activatedScreen.DisplayArea.Midpoint; + expectedResult = new FormViewModel( + formBounds: new(256, 192, 512, 384), + canvasLayout: new( + canvasBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 512, 384), + boxStyle: previewStyle.CanvasStyle), + canvasStyle: previewStyle.CanvasStyle, + deviceLayouts: new List() + { + new( + deviceInfo: displayInfo.Devices[0], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 512, 384), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List + { + new( + screenInfo: displayInfo.Devices[0].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 512, 384), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + } + )); + yield return new object[] { new TestCase(testName, previewStyle, displayInfo, activatedScreen, activatedLocation, expectedResult) }; // rounding error check - single screen with 33% scaling, // no borders, check to make sure form scales to exactly @@ -257,42 +303,61 @@ public static class LayoutHelperTests // | 0 | // | | // +----------------+ + testName = "Test 3 - Rounding Error Check"; previewStyle = new PreviewStyle( canvasSize: new( width: 300, height: 200 ), canvasStyle: BoxStyle.Empty, - screenStyle: BoxStyle.Empty); - screens = new List - { - new(0, 0, 900, 200), - }; - activatedLocation = new PointInfo(450, 100); - expectedResult = new PreviewLayout( - virtualScreen: new(0, 0, 900, 200), - screens: screens, - activatedScreenIndex: 0, - formBounds: new(300, 66.5m, 300, 67), - previewStyle: previewStyle, - previewBounds: new( - outerBounds: new(0, 0, 300, 67), - marginBounds: new(0, 0, 300, 67), - borderBounds: new(0, 0, 300, 67), - paddingBounds: new(0, 0, 300, 67), - contentBounds: new(0, 0, 300, 67) - ), - screenshotBounds: new() + screenStyle: BoxStyle.Empty, + extraColors: Array.Empty()); + displayInfo = new DisplayInfo( + devices: new List { new( - outerBounds: new(0, 0, 300, 67), - marginBounds: new(0, 0, 300, 67), - borderBounds: new(0, 0, 300, 67), - paddingBounds: new(0, 0, 300, 67), - contentBounds: new(0, 0, 300, 67) + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 900, 200), + workingArea: new(0, 0, 900, 200)), + } ), }); - yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) }; + activatedScreen = displayInfo.Devices[0].Screens[0]; + activatedLocation = activatedScreen.DisplayArea.Midpoint; + expectedResult = new FormViewModel( + formBounds: new(300, 66.5m, 300, 67), + canvasLayout: new( + canvasBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 300, 67), + boxStyle: previewStyle.CanvasStyle), + canvasStyle: previewStyle.CanvasStyle, + deviceLayouts: new List() + { + new( + deviceInfo: displayInfo.Devices[0], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 300, 67), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List() + { + new( + screenInfo: displayInfo.Devices[0].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 300, 67), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + } + )); + yield return new object[] { new TestCase(testName, previewStyle, displayInfo, activatedScreen, activatedLocation, expectedResult) }; // primary monitor not topmost / leftmost - if there are screens // that are further left or higher up than the primary monitor @@ -306,6 +371,7 @@ public static class LayoutHelperTests // | 1 | // | | // +----------------+ + testName = "Test 4 - Negative Coordinates"; previewStyle = new PreviewStyle( canvasSize: new( width: 716, @@ -335,49 +401,164 @@ public static class LayoutHelperTests backgroundStyle: new( color1: Color.FromArgb(13, 87, 210), // light blue color2: Color.FromArgb(3, 68, 192) // darker blue - ) - )); - screens = new List - { - new(-1920, -480, 1920, 1080), - new(0, 0, 5120, 1440), - }; - activatedLocation = new(-960, 60); - expectedResult = new PreviewLayout( - virtualScreen: new(-1920, -480, 7040, 1920), - screens: screens, - activatedScreenIndex: 0, - formBounds: new(-1318, -42, 716, 204), - previewStyle: previewStyle, - previewBounds: new( - outerBounds: new(0, 0, 716, 204), - marginBounds: new(0, 0, 716, 204), - borderBounds: new(0, 0, 716, 204), - paddingBounds: new(5, 5, 706, 194), - contentBounds: new(6, 6, 704, 192) - ), - screenshotBounds: new() + )), + extraColors: Array.Empty()); + displayInfo = new DisplayInfo( + devices: new List { new( - outerBounds: new(6, 6, 192, 108), - marginBounds: new(6, 6, 192, 108), - borderBounds: new(7, 7, 190, 106), - paddingBounds: new(12, 12, 180, 96), - contentBounds: new(12, 12, 180, 96) - ), - new( - outerBounds: new(198, 54, 512, 144), - marginBounds: new(198, 54, 512, 144), - borderBounds: new(199, 55, 510, 142), - paddingBounds: new(204, 60, 500, 132), - contentBounds: new(204, 60, 500, 132) + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(-1920, -480, 1920, 1080), + workingArea: new(-1920, -480, 1920, 1080)), + new( + handle: 0, + primary: false, + displayArea: new(0, 0, 5120, 1440), + workingArea: new(0, 0, 5120, 1440)), + } ), }); - yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) }; + activatedScreen = displayInfo.Devices[0].Screens[0]; + activatedLocation = activatedScreen.DisplayArea.Midpoint; + expectedResult = new FormViewModel( + formBounds: new(-1318, -42, 716, 204), + canvasLayout: new( + canvasBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 716, 204), + boxStyle: previewStyle.CanvasStyle), + canvasStyle: previewStyle.CanvasStyle, + deviceLayouts: new List() + { + new( + deviceInfo: displayInfo.Devices[0], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(6, 6, 704, 192), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List() + { + new( + screenInfo: displayInfo.Devices[0].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(6, 6, 192, 108), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + new( + screenInfo: displayInfo.Devices[0].Screens[1], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(198, 54, 512, 144), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + } + )); + yield return new object[] { new TestCase(testName, previewStyle, displayInfo, activatedScreen, activatedLocation, expectedResult) }; + + // two devices side-by-side with a single screen each + // + // device 1 device 2 + // +----------------+ +----------------+ + // | | | | + // | 0 | | 0 | + // | | | | + // +----------------+ +----------------+ + testName = "Test 5 - Two Devices Side-by-Side"; + previewStyle = new PreviewStyle( + canvasSize: new( + width: 1600, + height: 1200 + ), + canvasStyle: BoxStyle.Empty, + screenStyle: BoxStyle.Empty, + extraColors: Array.Empty()); + displayInfo = new DisplayInfo( + devices: new List + { + new( + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 5120, 1440), + workingArea: new(0, 0, 5120, 1440)), + } + ), + new( + hostname: "remotehost", + localhost: false, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(0, 0, 5120, 1440), + workingArea: new(0, 0, 5120, 1440)), + } + ), + }); + activatedScreen = displayInfo.Devices[0].Screens[0]; + activatedLocation = activatedScreen.DisplayArea.Midpoint; + expectedResult = new FormViewModel( + formBounds: new(1760, 607.5m, 1600, 225), + canvasLayout: new( + canvasBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 1600, 225), + boxStyle: previewStyle.CanvasStyle), + canvasStyle: previewStyle.CanvasStyle, + deviceLayouts: new List() + { + new( + deviceInfo: displayInfo.Devices[0], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 800, 225), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List() + { + new( + screenInfo: displayInfo.Devices[0].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(0, 0, 800, 225), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + new( + deviceInfo: displayInfo.Devices[1], + deviceBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(800, 0, 800, 225), + boxStyle: BoxStyle.Empty), + deviceStyle: BoxStyle.Empty, + screenLayouts: new List() + { + new( + screenInfo: displayInfo.Devices[1].Screens[0], + screenBounds: BoxBounds.CreateFromOuterBounds( + outerBounds: new(800, 0, 800, 225), + boxStyle: previewStyle.ScreenStyle), + screenStyle: previewStyle.ScreenStyle), + } + ), + } + )); + yield return new object[] { new TestCase(testName, previewStyle, displayInfo, activatedScreen, activatedLocation, expectedResult) }; + + // TODO: add a test to make sure the form is nudged into the bounds + // of the screen if it's activated near an edge or corner } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { // note - even if values are within 0.0001M of each other they could @@ -387,117 +568,101 @@ public static class LayoutHelperTests // (int)1280.000000000000 -> 1280 // so we'll compare the raw values, *and* convert to an int-based // Rectangle to compare rounded values - var actual = LayoutHelper.GetPreviewLayout(data.PreviewStyle, data.Screens, data.ActivatedLocation); + var actual = LayoutHelper.GetFormLayout(data.PreviewStyle, data.DisplayInfo, data.ActivatedScreen, data.ActivatedLocation); var expected = data.ExpectedResult; var options = new JsonSerializerOptions { WriteIndented = true, }; - Assert.AreEqual( - JsonSerializer.Serialize(expected, options), - JsonSerializer.Serialize(actual, options)); + var actualJson = JsonSerializer.Serialize(actual, options); + var expectedJson = JsonSerializer.Serialize(expected, options); + Assert.AreEqual(expectedJson, actualJson); } - } - [TestClass] - public sealed class GetBoxBoundsFromContentBoundsTests - { - public sealed class TestCase + /// + /// Basic performance test just to avoid any massive regressions. + /// + [TestMethod] + [Ignore("Ignore on CI runners - only run locally")] + public void BasicPerformanceTest() { - public TestCase(RectangleInfo contentBounds, BoxStyle boxStyle, BoxBounds expectedResult) + // primary monitor not topmost / leftmost - if there are screens + // that are further left or higher up than the primary monitor + // they'll have negative coordinates which has caused some + // issues with calculations in the past. this test will make + // sure we handle screens with negative coordinates gracefully + // + // +-------+ + // | 0 +----------------+ + // +-------+ | + // | 1 | + // | | + // +----------------+ + var previewStyle = new PreviewStyle( + canvasSize: new( + width: 716, + height: 204 + ), + canvasStyle: new( + marginStyle: MarginStyle.Empty, + borderStyle: new( + color: SystemColors.Highlight, + all: 5, + depth: 3), + paddingStyle: new( + all: 1), + backgroundStyle: new( + color1: Color.FromArgb(13, 87, 210), // light blue + color2: Color.FromArgb(3, 68, 192) // darker blue + ) + ), + screenStyle: new( + marginStyle: new( + all: 1), + borderStyle: new( + color: SystemColors.Highlight, + all: 5, + depth: 3), + paddingStyle: PaddingStyle.Empty, + backgroundStyle: new( + color1: Color.FromArgb(13, 87, 210), // light blue + color2: Color.FromArgb(3, 68, 192) // darker blue + )), + extraColors: Array.Empty()); + var displayInfo = new DisplayInfo( + devices: new List + { + new( + hostname: "localhost", + localhost: true, + screens: new List + { + new( + handle: 0, + primary: true, + displayArea: new(-1920, -480, 1920, 1080), + workingArea: new(-1920, -480, 1920, 1080)), + new( + handle: 0, + primary: false, + displayArea: new(0, 0, 5120, 1440), + workingArea: new(0, 0, 5120, 1440)), + } + ), + }); + var activatedScreen = displayInfo.Devices[0].Screens[0]; + var activatedLocation = activatedScreen.DisplayArea.Midpoint; + + var timer = Stopwatch.StartNew(); + for (var i = 0; i < 10_000; i++) { - this.ContentBounds = contentBounds; - this.BoxStyle = boxStyle; - this.ExpectedResult = expectedResult; + var formLayout = LayoutHelper.GetFormLayout(previewStyle, displayInfo, activatedScreen, activatedLocation); } - public RectangleInfo ContentBounds { get; set; } + timer.Stop(); - public BoxStyle BoxStyle { get; set; } - - public BoxBounds ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - yield return new[] - { - new TestCase( - contentBounds: new(100, 100, 800, 600), - boxStyle: new( - marginStyle: new(3), - borderStyle: new(Color.Red, 5, 0), - paddingStyle: new(7), - backgroundStyle: BackgroundStyle.Empty), - expectedResult: new( - outerBounds: new(85, 85, 830, 630), - marginBounds: new(85, 85, 830, 630), - borderBounds: new(88, 88, 824, 624), - paddingBounds: new(93, 93, 814, 614), - contentBounds: new(100, 100, 800, 600))), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.GetBoxBoundsFromContentBounds(data.ContentBounds, data.BoxStyle); - var expected = data.ExpectedResult; - Assert.AreEqual( - JsonSerializer.Serialize(expected), - JsonSerializer.Serialize(actual)); - } - } - - [TestClass] - public sealed class GetBoxBoundsFromOuterBoundsTests - { - public sealed class TestCase - { - public TestCase(RectangleInfo outerBounds, BoxStyle boxStyle, BoxBounds expectedResult) - { - this.OuterBounds = outerBounds; - this.BoxStyle = boxStyle; - this.ExpectedResult = expectedResult; - } - - public RectangleInfo OuterBounds { get; set; } - - public BoxStyle BoxStyle { get; set; } - - public BoxBounds ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - yield return new[] - { - new TestCase( - outerBounds: new(85, 85, 830, 630), - boxStyle: new( - marginStyle: new(3), - borderStyle: new(Color.Red, 5, 0), - paddingStyle: new(7), - backgroundStyle: BackgroundStyle.Empty), - expectedResult: new( - outerBounds: new(85, 85, 830, 630), - marginBounds: new(85, 85, 830, 630), - borderBounds: new(88, 88, 824, 624), - paddingBounds: new(93, 93, 814, 614), - contentBounds: new(100, 100, 800, 600))), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.GetBoxBoundsFromOuterBounds(data.OuterBounds, data.BoxStyle); - var expected = data.ExpectedResult; - Assert.AreEqual( - JsonSerializer.Serialize(expected), - JsonSerializer.Serialize(actual)); + // runs on my machine in about 180-200ms, so leave a bit of headroom + Assert.IsTrue(timer.ElapsedMilliseconds < 225); } } } diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs index 52870e8cc1..5ee8573ad3 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs @@ -3,12 +3,12 @@ // See the LICENSE file in the project root for more information. using Microsoft.VisualStudio.TestTools.UnitTesting; + using MouseJump.Common.Helpers; -using MouseJump.Common.Models.Drawing; +using MouseJump.Models.Drawing; namespace MouseJump.Common.UnitTests.Helpers; -[TestClass] public static class MouseHelperTests { [TestClass] @@ -58,7 +58,7 @@ public static class MouseHelperTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { var actual = MouseHelper.GetJumpLocation( diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-expected.png b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-expected.png index 1816a9125a5b3f95724e8955daad83e2a444c4e9..bb0ab46820d18af92aa54bfbaf7f1a1295ab5e85 100644 GIT binary patch literal 206380 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 literal 207229 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

|8?_ccAI`?`+H7lWfYK7{DQ{g>5 zW!9vue*3n45 zK9)Y1f46Isif$d#nEolnxoj7H9?GipVfNLk_K8{*tM?^xDt9V*j*A5>k!k=2 z?b^KH6#&q}y!`O+)r?s;5(c);ch+$*YcT)3qjj5k0DI9z09N8k$dU6LNumc93rN(_ zUxEu5P_=%76J0rab+@Ou5bOhX31Ca`?X(O5On0zYc3#`YwN};u!AvFxhXK}K95;$F z!a@j9f}w@YY_j;&vB*x^ds7%l@VA64rhm>Rvhq!s@;CTUu17uqC;Y!WM1wr4{dq*L zg%c(XkgH&98xS9FK>J?4Xuk(S!@>H9j;-h=y*F9UF8A^y3cK_vQMv}b5f8o^7+FuG z(+hnQpm`?4;a_bk`SeJSPUz9IMv89+0H5Szs069E%j9BYvT(p-Zbu8~K5kBh0sJ#c zo;pCAI++I0oNaydv{NT3P3p{`7~{+QY2wU|p8!QTM98G@r;ccBl3`I2V5Jk?V5_!k zje>@(a&=dSw_Wjg>_)-e(~CFGxCHtygeX%&H}TwkmOptGkz-LcW#puUBe?auE0-Jy zquhj1wLU$~*ojdaEo4b~N<4z0{3PvkPz(WBWtTw!rLdQ~hrhS%CONG`W$)9@Y^$Y{ zQCfa8=9bK|H=uNntR-hdM}pXz_fJb{79ZVrN$kD1e!iNkW>~&THACSi?y^H2l{F;J#E`CEeo1^^MbN{fIdtNqubwzf04HM2q_8OGx;0OVn zZ3sE`-a}-oN2L2f>MSK#)$_hBpeX*Ir+IJW-=5|I^G+vicU1RlO?I`gSjb!9!e-hM zAbSN2*sGxtll0#w+^@dxWFB2fzhwgS%0MUdIZpc_hz!`^jYQ+vT50)#cE4DmWVanK z9sG@Hxl|vgpgkU%I0(1q&UN>7a*31$oDgOIi>Jb z(IA2#-Mx*H5(x!CB&AWL8>G9tyG9QfY{1y~&GVk~p7)&Z`~Uv>?0N3zzOVa=v4SJE z${yF5hjD{<-8t`}AC9TlO3jrq^yC0UIBPf=gjEZ=!~fW{GPB-d8LnIfQ%|xt>%%W0 z<}eOZ5a_AxZ`urjc{ASm^bFp#+kFA$bKXza?2PhvPlER4$uRs7=YNuG^w&!~7FU)O zmeJrI1zd~_OBieJX^~j^MsEvI!u6bmX{Pw4z;WEaj5Z1BS7RTeFSknx<0yN7FQs3I zu?JSgqfbWneSHgo!EO-;Y2?&@3qb^3PB!pE>*WN zHVC`u_9c#~>)Wh1cAFMl&1O`P#nk;WEsp!|`iiLDQ*;K>Jf|sKLoI1^v@%&vfyHlP zuV`Jna)C4fLXr^>AL^lNjzuARu0EoeYx3p6*4tJe>Y&@(<>eDfSfk_WNy~8=cpexK z7d`lvM^u$6Vrd|dp4uqW2AM0(k9w>k99WpVN9WWv^O(t9stH zsfCl~nkvZ?Jxv!|syl_c{*OUh{Gabih;ahT7a5E&-cMPmYGUmYkI6g3r(cKsCFqD= z$8yzMz2kN+_rEW}U$Dx2?|~C{!jgHSE!!K(P(@nPw&FB==>@o5d*?}XNtq=wciDb@ zN_$l-0J5LoBv3`m`CGHneKnZBd+*x!-eMLGIi+8z7e%iyC}5is<}XYZVyDtEF+4a? z#3RxtbWs}|1i}>#A=z!SY8em4I}9@#H=YXb9H}>tQSL4YkX{qw-BhplD>JIhJv=48 zgQYcMz4vn`5a*lJ^ojc>UCOWeir^Ja4(iTe<*AuUr+ls`@^T$#Q%Gu6MgD=Fq6i@V z)T!VU*kjsF!zg=qVJs#mvLyu)LD?Y~TEh5rCVP0Oq+Rx@(`S6=%$}VGeJg{>_Nvlf zC4*soKL(mQ3Xn{d#2B$i4Jl%7_EA1@bTm@(>CFmTef={h=eEFSOrmRBaZ2})_KNK=(hgeB&HtQd>We^!4;pfWpS#I@ zC^ODYd7C=RHXs^^(}+F**2m%>R3jtieN4mHv{EP`$vnx)@KV zD@4gvh>6{#Whbm1CA0gIhwC+HN9@nF=-LM9N>~}ev@7E(SM%i6=Ls z-`eYY-9OA>fq4M&-QUmjy5aI>y@d5FJ#!&4VPe(puv8`nMWvgnpl*-HQCzKye|LXp z?H;2?ZdaDkoVN{u<7um_=IB1S7w|T}bru7{A~$h{9&s@HzQ0PluR#YWSX+Ps^{*?+ zO;g}vz-i7l??sIo?xj8;qzNWenJ)u$^o+96su|O%%=wuRgwupSfB1=umQe-g+ldqNI2Q>u-QoEn z#A8F|Oo9K2o7G@!?vvERs)8M+YWq*NzohJN!g`UItk@3(GLCdW3F9!iuaXP1<+b*$ z5%r~o^z&E|F-Z~k!It?*&8+ja-m!;;EH7@} z?zy%{GOfO5Um6#U27R(o+S_GC$xwwAgCyWj4)UK-hi~3-voD&Fu5efRe0o|l7v|nQ zJoM$3Tl9&@#dy~3!E!J5&+3uINl(o3pVf2ArW7b9KOkA7U*)!A^@d<@9XjY)0Xk*d zdT~4A!8aKHu+%>&Og1*^9btzyt)*7Wnk*9&m*U6V7~T&grGt#}8pH(dJKSvMD>}l&5w!7%F%g z2-#DO>AfB-yo&AWc{mnv%wcT(-RAkWc|Y4mq~=WZYPim`dXs*nC|&%fN}PFhbQpit z`e$589kCfzk%EhFG{07{CO-kDQoq23sZgi|72-!-o8s2an1FLM8!wA0i=_ls4ez6S z{3#7C5|~LjqWn!iE6kRp{SBU4{4CSLsgzL}-mTrQupc8mOtJ0U;PkH?x(EsL2!&Bk zB{Y?28qV$f6A;&5Sy||O7p-Iac2%>pT+?3k!5Me^%rO7s@Np(CVwox%lEqN|N3oQn z6k?WdTRKK5+F03lde>opqm3bVZ<1!~cMvS_A9_^*(~d^XzVTOTT_WJoo0MzkOM&#* zwHoNf^X+|pqKntGNY=JJc5J-nRF=7jpA+UXZpCz#*ZAoX{RSPzuWL?>%J1Qa(~h3t z)8QT(>+t%IV3GzJ556tq`qV1{=vBk9zbp7x6LwsnN$&=Y<3-nw0o=sN-9yA_Gy-SkcmeTbr zk-v(HvdIED{D;f8GP88@vvkRf%OqYy3C-NF!o*P=)W?(laP6ZkM-;b9{Ue)ue^_)_ ztSDImoQ?P={f@|LAv34Z`uuHNGtvDIE|GmmA@`hKVsHN7UH6GJ$ETH>oZ}Tg?y;jN z)7xsSuH;N_<8086aV!R0&HN7GN&wIim{KS)DC@J_7Z3}2M8aE>FvKBp5^Mn5WqOpw zi)*5x7#Q1)=@Vn1=EEFgqr@;89HCStm@=mZcd~IR&eBkgkHZh|aDx8~MoD$x`tSt> z(?Jznnmad6qCc#bAj$Df&`qs!^k*t-T8`se-d8E_DGhUmO|mIh8i?J%di_6!nkoxe zxj=~($UtMyAZBr%bqBu1)+nydhRr5&Ion7T+fLdV2n|dJ$zkKE3$rDLorn~zM&m5% zg9=*Wl<3rg*t@BfTT7LIA3ndO7@x}(i*k>_kLpoj1Z9%pKkLt$q4g~5D;G*ggouV^ zt>T`6&3fV)`;zJNOg90=UhOWN#5j-7fQ_~`&g{OAVY>mW8%>Hk^K-{SqiMkYQXAE9mmW!#F zA*WMOLz^{&2q5<%dA-FZlXpkfKhylNVnriIuxhk%zk#-@X;=s4jj9*>wU)(0WZZjL zC*}B*YiJi?=9jh5kk}MUk2_+&KfT$d<{nkShAEKwGkZMU8t>DPBvIo+(q^F_KqnFZ zEc0$WUR}1tSHw1zG5fr29sd~Ux`C?DtgCS@b4{BEj7U^^>)%{vr`+nGLZCg7-Q5n> zEz~b->4N{%ayfe}1uwp-Ik;S|*ICCtLf&tIF!mf>2GGj8*d5=@QXv?_J*ggvPAxt9 z`bV{2DOYwh*_-p;|6ujU4-e)qmXWmY%}ic?oH1{2Dcah`7KaP|Fe><&(+M}hZJAt6Ih-*Cix zsuAIgc-oBFdk8$oY@mT90}(afwd7^zuIiraH1+S>I4ma==&rI!Y)xkUx#vNbI3uW% zg^b3~{01O-EUx<-;BhA<=sT@a)DHg2vu7UBMqpdnArYNB+$IU43ELKw_EbadN8gFE zeyh>iwtcYlp;t~IaS*Au^PVRoplrSiWw9Fs8Yx7ozTCt5dfPoj4V;Os4f1?$Q%~4j zet8V^=m;9^*%XoZMIl={e~SNuoZu$S#ER?a;@wwQ*i{hqKFExd`}gaDsG2dcl$8Hw zfXbuSzk^GtJ~>&~Dhw)Vd&vgeQ9?b=YBO26xwG%S0>Xb+owXj)^3DBBB*xA+%~iql zI8tqOSGukAbi#3YK_;?wx}G?w%o_wrod|FnFB9rlG9!~{mtEX>Hs`7+f=lnYn6qz2 ztPq`jk|T~%k$JhPYH2)juB;Rej%i@?#8Js`P7@kDmS-Hr8SCTmX}47GN-;{K$8A#U z?q0-NbI{E7wbSV{Zl_gG3yZTd#q`%?=;DL;1Ej?l4th)YD{6^0sC_L}_#&%)uRKrY za79EzmFPR&%7}dUi$_*2d%dkWvI6}xDn83S&uWMN^Xwz3!;|*=X0zkub7F5OWOBk7 zlotq#i#*>2yXBXDWlg(nA|;ceb0(x?U`3ki-MBZhjF48WE>!gozGR5jJ(E>Or`{kU z<3C9+CT^D4SWDbxN0|4|wBl0iXM*9n(PHzk6n$Lkft(Mzkd)Q*=ZT6gG3~vo&M1<6 z8o%?1IDqG;w-)w4IaB2c?mZ!)h%*DIc)W=DfRER8`}Bhot#UBa(ubK6#wWF;IO0FA z^Sv*8cxvOQ0wvYzpZPfRx(<@)VX+)aiR|GT*}2azbIRLiU!HgpM#J9l5Du$4i>uE? zrSm$z6`^I%H&&I*x!X%;OwVjX`IgU7%9YR2=ARfCv@@`IySn0!t0YOjxA>v_U#L#C zX^9A(qE&Lv`Q1b>LirsSOdqnCO7LeXTE6?VJ;_qu#m(aQ`G?@Cw!}N0J5kw1*=nEh zDtITpukNVI7K(VE)k2p=N}xvGa~Oya}aL<7jBjw7l?_s3y?2 zVk$F>t;TLbC~MDtJYelW)0va*{a9|qj0825^v}1JkxF88qq*tgJpI60Q_@U4?4TELl{ zP)n7Sz+RMe2!%nFMnIz!S(UaHy33iWPQwi6>q{t;)C*OMKYMs7BkACrT>t_?IjW8& zYqBF>#<<`6)$vStcYR2%eIeBHtCWlB5OYrL+pwVSvIwTU_msg5)JE5}5sEVQ>nAa0 zD-U%`^two_J1+s=VZhhZ zHI0Qi-rT?v4`6a$ppn-owa!A)xejGSn;F=eZtrR1Qj#2)_ZN|})=fi3-)Fo8aZ+)a z7ZI+ZGBTF@>v%LF^mcTFAbta45TP!wTjbYJuFNb3iqwztC0YukcMHfuW%8?rGVF7_ z${WBh0BT7parSO@Kpl39Vp-asJ0BU2OS}fSBOhxP?$Ct)Nc1*wpfWQc|CBV9-lgxU zuBSTO4heiAcgXJT0CO?dZ_?m5XreCs>Ek+Eq$t3Lb(+qcHWr)(;QV?Hbex}iI2^DZ zWX>qhXjob@-vM8;>7C>*$gK91ko zN`w@yMW(-U9MRGJ9lJ$QY2618==A4g8Od1e=pf*B0TudAfBI{_fZaxC*8e5ouJLW| z*+`w%cu5{WkuK&LRO7g0RCigmJG$ecUrNn>$Ucb`$c*rXpklW#G#zK=@RrKT+@Bv2 z+;s#2kKKo4$^PUX7GqXeE-s*@NDTCCU^EHI3Hj8FrX>(I_9wpTa%v}EoES^7bKJ{) z>63rrN^);QmIYblXThL0c55=;LWg3_ke+|kdOmlViK+hp_sUySPqKoYSd=OE_Gt7X zO0xE=5$V^n(7Xemx9RMj$SdaFZEq@Xx!b+(3`_f1-(={%)?3*5?C3%OaIHnJDDyVX zqHtH>VmfyFQgL&(sI`tEZ(Vg?v6IU}{14{@h&d*&nse690TF48`e8EGS==o{_u4A& zUrES#>l`Lx%#4?y8IoXg{wD@Ns>TX3ofdUj?tUiSv`U)uxEw=N)XLKsN!n?|KCpba z2&@7V*ifOpL1EJsAtQPzp(BPKwY`XXn@^4{3=XKSl2@eEOdE0km0CG@&XIWYplKx33yhi@hEV zvjK32x5^=e#y3=0ZBN+~#?~IkcAS9lh$fLM=qBKi6!`hl^PwEq<@b;BqRbvHx)Z-; zwX~PWmxz8HA6u4tkcjK#g_ovfOgR*|8lT&OoCyWi)MO~OqsWmoNS@WKxz6e=`&Pu? zFMks;)kMlY-uh*~7wzyP_2x?>7G0qt1>G4GlWn&yUh;9P^}WWvC_m9%`dVN3ni9|D z&%k?_muoe$mlf@_cHxw{e+#qzPDq0$3Vo=ft&ISALA_OoA7cW)6M4lpYs)|2C8+n8 zD6vudOnt9uT}Wq3ELNl+)I3TxzcVeV>Zl21h<4Pzj(CP75j@Hmv*#RiG&MnNunjl) zn)q(YMZe5)6+8d%ce+mF+-2!%_|pDKOTGXYV+VPT)@X%)xM@zVEPc|@q8nCs(eier z#TLPSq$r1_3gx)NLbF@nyHF2TJNJBZ9mI;d$yi@DMQR6R%|fRjo$BYc(a1+t(i5_E ze>G-13Ap}RP9l>mcF<(sS4hnrV{ z+HQtyhUtc`rLlqgTQ{dsI*d^Hj?I#DeSA9h4qzz%c9r$?r>0869Y{IvPmcnMgghcK z#aE~6drV3$mtF*3F@7B`pSctj0-`nuYE{5(A8{x1^2C8-*v>>wz?+||%-Klo;v2Hd z^IQ6noU3-8kK$}MUFF2_^{_8}ltZ@FzYW3k%xqeO40d0r4w?(pXKfT2D4yr{i zW=I_3u3pT1S9)9V-3PJ!AjuK+^Lv@@)bY_m)gwymPVVj$HSTCDYu*JZ!wZ8<&a@8ujzd~m7k zP44R%!3ikTyO`b0V?I>nJ2I4QtM8ct?ZRW@ufcORNAv5)8d>zmT=3{X4th9!6G%$7 z5lu|$V^leqgJFV4Q(6Z_OL{0|a5)dl=-#zhJvKWB3E1Z8>1!-u`Q{9grFU0U(7xjs zdG@8^IqY%Q;i}MmxB$W+koiaWnPPc`gzd%nR4CU-31)g{%7~)|_FiZzb9Gx;Df*0{ zH@C+2eA3uGc0Zw2XXJ3*?(13R2KwvG`#k~ZhCG7bUAU!bX2W_z?xs()OHn=up%N$y zFEEL`&@E}J;yAkY7bePLBy3wvUTLpc@pZ~+>$f@^-U~wRc)fLd74M$$dErwmRV-Tb zP?Q#SY@L65%b@9buyX3}YM8CjTe%1CLD>??oX58VS>v^Pe_1QZ9jlyPGLmYwe4MN6 zT)KwDV;&dW4T@?c_v%6@NoTt`3=R4wV#3Z2&Vg<<2XDSE^g;x$+}|l-UL_-?%vFa4 zMvj;_R{Z1ZBMT7Ovi4M*5YvHgVz%w^F%4IKZAx;7P0JWNH>wPSHs0gSvBAcJwb^B% zva~AwznyxPUkn@FVV?Vt!DWodE0XFX^sd)HEnlg0bY)N$0zA|>i~EYY!aZM#?(pK; zq{jz@jxpEojQUQ^O_Ln!5p&FYvgAJEY@1GZvCAp$jUgZl_9{xhcO=ZAg`MdfO7M za&(nWE%jr8SnOKj18VXP-z1^(jw&%@y()>qC67X%7*OUjDOiT<5|FMdliil_vMSh9 zd+h{2lv}3D?W-`MuB31MwIG;_2;7mK)(0E1a5vaTjU0w zB_psR;RB`|FQ(b_fnd+Z2>E(LwtV{53Ks%y$pLh+&+WG))Nx+m0vu(3YUh9(GVC%T zf0$cJWlH4vw&i-Rdm0OC5+rMJ@6W$6W3dGBNa&JhE|L(<7??V?(qplUwrP#zVjR@y zZf@BdhgiSNQoqnM)YT67QZh#oV0?HK`yp(~Dare(y?`)NDAQp0qD>4xGWWK37mo%X zDDzuN3oH_3om9ujg?)UHZJ|Qk&UU<;7WBG$#CJ2KC4^@;-qpSj*~V|>G+a#WCYZev zo8d$bQOzM=a!SC5`NPM=s(kZYU5u1ux10XvEj?C;)xQNRZbALj`6xNe9>(QK^%@(+wcIEq z!+d!L(7yIwc%Sp>;Hg*(Q^H9nqet2Rn;*jzObbI=4x#Z96Sl3UUzIJX|JKaRBGqyRO(t$ z8|$(QNDN~A2cgvy=v zB|Gs=G(PeD*{vVA>iRI#7$WhnNI3@8$VxJ49QhlkOl89Dc}{*RKZ`zCZz@_pV2tw{ zlcpy=t5a8lTgD{8uOV-_OB?QRkvUSC9;R(rkUy0C?x*Mcjr)y z;L=H=RpRz|X`lVUp4lGwAYe|czO@%qm?PEm&~LS4^yLrhlxyq{l=)F_2IosN`olXK zwNK?{$m*R92l)4fQ6YX}t0a!hp|vs2tw!J_=*@uG@c&+r>s9f&zmfdT1G`OjI#Q>d z5XnK<^B!Mb0KQ4z-n%AwdbIR5j=Ge~hHD?HgNu9g-2psPU`|vLs%FSFo+{ludg`oH zRn7sqx_U5r@FWQZpD2IsBJ{7?))=Q&Tj8M^jIwZp-2Q!b{D@cLCHLE@D>(3I*5f!V zb}@WhOSBS{Rm3U^YX%!szm47OnDJ=bNK#LNH8l5Z9<;N;{wO6qD!Xlg7Ye^HH{%K-H;Z;X)r*xycnYwnPS-F3cD-193i)%&k2pwaglj92#fq zl`Ha|A^_%?MM8#c%~6tB$})kWa4~WsZ-S&;8ftLI656XXAa&u(uH1V#eWa$W6Z(s>Q! zBPDlGe3V$uo6rTz08kE+Z5Rc}sPj8qI5?+&JKoYC67Lef$#N7x(`Nu}_7&CvrMS3Y z%(54Om}YSoVf~p!k>wT&!cldVDyjkAzz)HT8^o7T8&2Lp8)N+}$({h-tXIrq+N;0r zw;?maTbyU%ZGO=I#79&tb!Pc$o>oVIPOxL6#()B^oq#pfOV!!j)uk9G!A9k00Vf*- zZzKogqdvuQ-4ho45=+%j{)fq&!lq}EV`u2MxbIuB)G(~Tr!0DFFRBMHf3d03gT@>w z>_gm(-^i>8BF8k8nWfFMN`2*{g{LlHB!!*y$(phphv)$9f_RGZp8f1xqD38+Kf+z1 zYoou5W*FIHS)8T5t!ab~tBTGro=-re$7MfcGjY2|#gxy%2O^E@5c4~;C)fw>A~X?59kB0_5G2Bq=fx= z8DO%tCc?QcDR6!CoYhINxQ148x%<)cRmlwbA7>`IIZk!@+13+(cTiSGN?F@08q7DW zKj5swi_?N=8f{AG)QtE*NtlU-01;^5qLJx_CNUKFZw6f*bo)TQ`XCY_F_0n#_-f%Z z>_g*g1ITk>o-V2}=UT%4U?O62)0c6Hi9HnC4Fxtr_t*w@KFzW&MxjH|;U2E?ob=x3 z@&2(Q*OhK#4v9cv6(@b}o4Nt`G}+VY~1BYE;-^Xiy^ zmP+toj{&_vOR(F^G7~0 z7Oj`w)WBEU8DAc38n!++#ol{!w0`=wZetapNC6&CYe)hf-PK}?ts zh2x?%!Mdc&=`1MxlMDE6BR+sFnEZ-Slx_=vXVIC4t4nih3IK62Ty&llezV(b3!k`* z(+;r`N4g?aJIz{tkdhn!blW;1p{kaa-M39VoPFWTwqq8Aa#UON8@ZXKJ$@~X^uedB zqmE;`DnqagFXc@ zIZz?&1vB;~GuYlY^=t&yI?&ZP9&Vql6dN?bk_j>+7al);P-FKpX;jG(-?8tWkz+Lz z?b3JR8O1+oCBL^D4;-t`T59Fr1G-QwNbY}_$Q-WOecaiHg zPN`JRs?8R^w*IR#(~mfH=#mX1&yQt4{kW5qYg-VZ{TSbu$G(W6(J`H+k00m`yre}r67Mt#vxvk{xhY(KjanGIc7STEm+ zT3Fz7ILH`d+dC*KtJz*Cx%g4DJR2~^)wa@(-9S?ZeuWz@Y*xdsu)Bdp$f?-)&u33@ z8z>BLNq5(o#3}X@r#N{t`P4ptdm(qqKs6|&yF&L}13vfOU!i#`s1SXT#a1Z@^Br@qfWoR z9b)B1-EKHgFQd~Ao6frQ3mXOe8-o8w~#Z=rk{^qL{)e3Dmym*X_kekUF+Gd z5J*$Qzn!Cbc`XsmSwHWTHhei3Uf%f)W0FKZqlSM6zfQ!E(&##kug!W8q7o-hdXLYNXhhie=;b z+o71s1Ht#Y9cVR;XPIR+Tbl?WUwB5_zxdA?$6Xbz z5=oG{2$mZVD@!dJ=wfA{g@rdp0wjcP$K;`SGPmKOcv`PD5`qK1De41O1TcOUMZzJ( zUPSE-*}|T@jN5bXE%71SgUEqE#a@#qmRtYZ1)&TOE1||qpkw`BMXc;VwQ}yMbjM7M z*O-R%l=&XT6_#Gd<&Zg|mVNY2Xp|;`$MV4_9bk_WaSHy_&9YGAD7f=GEu0BZJUbi8 z8nce13U+fvV=P+qZEwOoeR_kRji!J+5&BA1o*Y1rPbHfG(uWL$LZ{rMx_EApWAE!7XsdRfZX%36}9pVPr@)1>D zak|>W<8qXeg+~(gRrNb=nIGlrU8%Jq(bZENSH6-Cu>3rqk-~f**QCnu6SNzX1FHJ* zAYy&=C1sF0CRI2KG85(zD`@p+oK4bR7~xH2#9pI&y|C|CQ8sQ;XAB#xu`JiqVZWp~ z^j#oOJmHzs#?6y*{6xw!wf3oefDPEh7tAz?GDF@jK5$QtDI{Lg=hK>nCMx=t;s@kF zH6)(WFl*ePAvrzki=tfZ>*eI5_ML929*g5ppfF&O@`(xq(5}61n~WbZ%1Occ7dTZS}HJA?v%||3Z*1JU^U6U5R6-uN-bM76_wcH zY<1Khb|CbJM{y?uab@x1*BUV%6}3mgD5C#LA;l;NL?n>2gb%z+qU6Di!&`{x2LOc6 zbUE?J@VSFaVMLHX3qNjx0qDTn$RZ-02%jHz&4wFGNwdUDfzL7lI?OEYJu*E zCqZ5yuM+Iif5crQT^$95dl}C+Jx`la0%L!sqyINBCCq{Xw@S~ufvrk@br%ZrYym`3 zyrG!J47We9mYmsKTYkjW$_1d6w1%ED9_y~iTZ+{`Ag&n|8W!8=sTLe$KN2ND7L!3^ zXr$&)An{Z1o>>yrbw1ky-@iugNT8fwj8H7O;|N1<#NHLK3-@e7k=%qMK$}%eFD<47 znoA5hNfqoOKv|Of#L8Ot@n~cX`G2l*F|+sFfbnR=v~j2l1%A5P8daXXTW_d5^9-&r zLL`QA$H~uqlwVyEGVJ8^Ig6}Qe1*Fi_=&x}51z0s| z8Xrg|I~0EiJtl?u)-luXerEu*+xkk~Km53o*%sm3wCR&WVe>F~n<7|8^_uvZm9>>! zw3j*EvSi{BLz!f)j%%WVEw~kcqCo05l1zAyw#T1)8s?A(JGu4k?J34>KI?_U3aeD5 zp&utI{(Rz;e^Oh%*fTfSYSh@WF)=>)=Jv%K^V6 zh?#2`h559<@h(>%X3wGzk`h8=lj|DdwR(P%)qEF#e=QY3az9wS7@vB8e2<*^g>J0FW5FR$r|8V2x;b&Gy>#?qqINx*V!gFhzC za!q^vxb>CYC zl-2vyznrZ+C|-{A-uQk^66Lvdtv*UqZzM7R=zG^c+G*yXk`TTIw3KCk>i@+5`}*Y9 z=DFlu+1E4^K?5RIqeag|cTSCy>ATj$|3+ABI4&JU-9-1W$vARjmQ+6mrDt*|ZG8Vs zqN3$@#e6K&5&ei#COg|p!cSSP-rA98iRaA|+?8C5lk5%Sk=eE-DqI1d?7=2@cDLE` ztquIjt(hD~hBWIvt9O($dd5xmABO+ftjpW+E0z7j-1&|Ju5DGZ`Rz!J)u?E%MB4$8 z9$|l7Z2p_AYIj7#KWO^6)4phV^Bt3Ukz$~}Qm+M5ja{o+`^!JL#WAUaBXc>jg2`d{ zyiG&+lEh`VAb2|4!g94<>#bA8sl2QJ9U+;55gf=^FW8OhK!aWQ?- z!&N-bImf%#@?}Nr)Pfb$8h^}{Al!TDZh`gesuZ{)fjz2}esJDSWjzr&y~EwqRF1%8 zgrI(($63L7tC|Pvk3C)l8vYkp`o1mKW(K>}#Yei9mB|deJ$t?Wxy+J&<;Hb2iylBL z6RjOt{vs5A?IP)~qDuIW%-Nr#>EUPR+j1R?{(oWLCRVohQQ?M>CriLcwrrJN-Ns~p&R!s4=57X(f^K#@y@sna$RK#^I+tsGPKxtyJyb8ET`~cY<%x@A z1(DUQ1US}T?WA=+mtJOYsb7ycNzA&rx96s04txc@vb4z5$A0h4m^>(=6iN>~ZiuGW zQ>cK|Z^mgCWuia1Pp3@82AvvMHF0L9AA4h#0}h-u`M;AmgAX~mJYMr zbb91lvVV1EDiyt?m~Sa(mH4Vm`6Ir-mVMb-sofD9*L8O!@>!gS+-FI`Cvg-o%yn6$ zp-ZFv+`%kVn8W*WJ;N-7!mGz~y1o|#FL_mVhKRrFb;B;YP+BVP)f$aJc7h{!3Tut| zYlBAJ6`L}y1~UJ^fv{N&HBBhnI|TX<*)3B0_b-m~2h`6R?<6OMd@*%t;Nv*2MumgG z7pd#G93t_hYeZHg91N2;heQMizrbe@F?`hK-F(;>icA`R$=&_FgFujwGYORxP=Uyd zCkI~d+M$uZ3{S$uF{Iy8wZ9&r4o@86Ee|XDJ4Z=Ta_5Hz>}N_lIOK)(MDYJdC9x1; zq6h4SaWfK(&Of>vHXhqI(Wawrdk0^~=X%7ho(}v>?h9ojH>nDFQ1LTS0`baRm5sPp zz9mRJn!N+!H6lS|z8?DpUP9Mf8#IRiR6=y(fCNev@_p$3U=eq1;Fs zc~R(9JyRs>m=g0Nx86%v57*9`S2FaXk2F~&jDSVMo?=rYuCd_@&jW`C(Ru1eO6d^` zihpTp>~w}u;F*23s}gkM+QG*%V2NanWU)HyB~3{x*CqF*LFs(QtnZ)O?M$8k0iQ@D zH+4WT+_TOUkVjD1vPdhLW_>p!76g1C3&4G)yEUFd*I7UB<8$-M7gbq%QNQ)wRdScd zXa9)a=BjjWT+?th?LJ6%l8s#)elQOXMHmo*nC=9BZaG@7f9@o8#&I8Rg&k=_zYgdv zM}09~d2i+`Tm1L8G}CB_(mzH}Y7Hp8MXP@>HE>6|lniF(Wqvu}>q5GEGcF7nBOp~C z)Aa*{PZ99!9xO7x2M{RQJb8Z+EppK}j_`j>(h~b(KOxks`R|KZfmFYl_$Ph~s3(3K zbx&jts|P#QEl1kpAtR)@(6jnYSE#~98>UmQw3R0v5ia`icWH~yD{0)38xwkc#o1nC zcq?Ic9p>0^k#(JGQM>;QME!e(@hXT%0Z~u)2oc2f8;Mx;?jHdVCc za^riepjqHi&H4|=QcXEI81(s6?afPo8Xggma^68ZX%B-2hr620VR>iA&qyw|H)R%_ zRI*zMZeuDW;RMtQ<+G>gC?slW!?*EEPtcv84qC5h4X)}|{dF!^R$5tG{?597bTtcbB(1|dTna#z zk`eLM#=$u~IjPEU0`l zjE^~jS!q9PojM-sdO5JV`jva`%Or23a?}El@%Umtx1%=5-*0Z`n`4KRsQkUdvvn9M zFRN?8I3$Fli#wrWS~n7j7Y__N+sp;@wWAh<_O@NUZ7?^~W1aw;IsxdT!>j7}kF8g2 zsLuG(zp~3|i^pF%G5W;#N%vV2LP^ZIf8Mlm-g%o%zFqgUS#RK6J7}tj>ly#DA?NM! z3C<+lTjq2b=h7zCX9o(hnDagIoTz)Uo2aCKOY4MR>yL^8Puk0Gf$5A$rAtwbYeG=M z|F+3uKlgACBvkGK$HBI#faTaf>s3e@9Wiyj66E>3;w;IsoJuVMF z8~RNSt_Jj(4Pcoj4ROWF9xp-O3ZORXbhyiy%>I%P`=R;cDYF zKF-Dw%DS|c?00mlH6I#scX5Iz^@)|c9>zW8_<~rE8>I7fn{n&?FzkPS39pHv!22vY zAYav=PO{KjbNK47S<=X5&qi^^DjT9EZTp9InU9_@$u_1LR}k=b0+k%SvEY~mHhoiR zBU1;*7n-a$Cl{4Cynp-JtQ2l?3uy&#%!evI3#3OII%hR05^&30h5uJEm_s4l?8t(r zMtGC(624F}l-VB%lzGM1A7v{bH5x7REN?myQ*}M`%gxtok!xUv_IORa1f>%^m(hj5 zc`*D-^g!VE*2gE%Z!2Pl5SwIQ)>(612l#$$_kXj4M>AS8H&}Z5A4I|nk^ax_KyN#k1{ebh(~ zFqSzvm%Tr38^p@Kq=ycW+)0@A?Fso@(-XILZqV$%m!~fNF2~0s$ z0o5={X_p1@xm?19+v$QZ=O?M7V16_I?QiR6gNQo0F_Qj)))Y+ldm&Pfb2vn42`wFe zP!_hZ!>y{zcv8RhRr^I-SX(b12?q^w(Y3Y3wdd4-#b?oZy@Ono3^l)g>TY3}-xi5Y zqME(w`*`k+2|~9CM6L#`P_F-SbfvPq-bZyd&~68z{b$4VpvdMqb<``WP#V&4JuAhn zWyemwCXnFG2zwcM6LxPpdJ)Iib~`XeQ5UvCX0wAyR?GFXd2ZEa3~)7Jj;4hT=4~+) zv^_@C;^jsPI|f3r;fhVS6|}v(p|pheJkYtF#!bg;Etg}F$RaAp^kc50B&bWgVn;9V zJp05Fmc?0mb=tiSQaUdv-%d6~u5$2@Zg@f_RD#&C`>a_Mx962HooI^y1HTai_#GH_ zd9#}E93%KvEPn>ha8c91q_LLFOfU*YBA)zaF9ZevJx!+7$D+6!3KjMXmGY3_h%E~}+e(K^UdC8mq5*Cy?spn zN-4lWnNwBolOU%CKuVw~I^S(#9mF-P_h{rl+m5wN5|GfLe_vv+P}iA7q(Z(Z++Ck1 z-u`QGM#b#@5^)BXjd{@5aSM+H*}(k9C7)5%4Gt`4&eR`RrA> za(QW%>`lDv-l9PA1m+zdma|k!y{#m=d>n9bbjXIhc(bBdJL&!MSLL;)bK}i3P{Gti z(nVuRvq(X)tJhsfYlbe{07&^6!Lf^Izabpt2gHD1~4u2qQX ze4L8jV$~VNS(E3%P%j$g20!H9a78LWi&CIp^e!Bj|4(aQ8rD?Ot&5_K3W7Gahz!9- z1w=tmnUe@8qY?!XX@*DxqB73}NKjB{Tjs%tLIN@m5&>m01OX)w=E=wuKnQ^_1Tqjv zNOEI;{e9>3J?FW_pa(EUU42 z7y`eyeICbg$L1rhWRe3%HIU|+HE(h#LmyIt-HbG!H%pD`UvNbJYRx<2Up7ywBHo;+ zf)7E07sr(t(UlHhINTGfPp*P^SoQ@z`&}}4k-FHTD(>z9+WtqI{hviXm^C3?Gm)YyJyG#k9h zzqLl?>U=qNo(d_w?n_drJTYYX*(g%YQ}ImrTuBg7(-)g-I)S@}s$LwX7jSG)q#bk$=jXY(3IbS0sc-k0t@9((rw-vEn^&*CSc&zqJ1KW!98efacgD6{ z#AR_2_M7kf!|}7*ooiyth&06-KMgHtZD?umSSgMszFiHI@JM_AA}sVh+q)~YoBeE| z1Q9$&yu1|EqBhM!dX{$+uLs`%LA`pl()+wmqpb<$bFU=GXXPgHYn*sf;@P$#CDp?B zySK{qq8X??BxuQVA12N|uL+0Jv8)22hDvM%sOhE1kH4XG z-Y;{Pm_{jryOQPgHRA(BixSc4xDZZbxl3e9Pt*Hz6x+I`rI3Mwseex=TUO30#vZl; z-pPyHc$^Y*E_ywf_|zKwE*wfR@TB&6rY7Z$2wA- zVI6n-MxK3u9-K`LHvLBFIkaiOqqx;W64qw~`(_b<{cv!PvEYwMQPBzqKzb~IbF{M> z646#FnQXY04No=o+zt3{kBJPmqAmJA%8BVRZz}?0b)vaW$OMc_faO*zyGjzn|BPm` zS{gQo-A3i;x|eSQ8Fj8W&mfKzh1cK(GBjFt>_6a@6KJ-z;sutrF zZAD1eS0Pp**9qX=O5Ol&V*onA$Z$!mbcT4NY`4;PETeB(6u@2k=r5eAHD`d_Eufxj z5Z?S~mV>J_!uR8}IGDH8zp&@>{dT=Gf(1}uOXQb>vk6GS#T|-Q%b}{Kh2r%%fZ*Z^ zi@w~3w=vYEQb8lzo$fag0~1nGQbybF)+@uIGKx&Px^z99@??Fu-$$k|63>gb%gN}UGpd|pq{Y*fWkz6hjkBKZz& z`u;ez)_O2q!0&vVigJ)YrpR7HKJDdu(BAbPZb>7g#-0*F7geGq5&Hx`{lN#-t!h4{ z;kW?X?HMRZ&8My)XDet&>hbP;kxqyo_7Zk67x`ow*8XKS$G1*u8oTIJ2GQpiDVMa+ z_vjJwo~--FtoKTt(C4W_xWb4f?udri2jgPlc4$>HXHF?$`GOez1K%Fe-vT@vz4dE_6Fj<)eqDC9byT!8VG!V|WTJMYx~Ihv$-ObboLN@CTa97%Kl)uj zxFB`(5c;*I%~>CLY4LIZnl4{YC`=Dr%M1dAUN;krl*L^|EoTYzM{nGgTN=1sh~b7B zR4rR*1Tle7@A*u)57VVetIXV7dm)`3))Fuo%e~4*-9XeTgrMS^wEJQ6SW0kNQ-u7+ zan+)p51qt#?{tb5v&376i7{CC6~-U;sB)mfI)3iCTGpQiQ*DBfmX9=2P%>xy-%jtW z5KLH2Vyp*y=&m$RlngS>>ulCljS3LhfmfS_igR9QJoI`s?~5T=bCKTvo*9+Co!!&# z5c&1{g;I<;bz40~({{Zq&RN&ze4NdT>c&*0r04XNhp%m~qm4YreLp<+7(YZTJQ(== z{=OTxL38d)-Y*T3jnN>if~PUKsw8^oxm@x1KDyJ&S$AW68s4_aQ`62+R=iQFYcV6T zNW;sg9LgmO|SBM

6JTh^-Dkf)pe2yuh%QGXJ#*+zSH$q@oC<=uHPK?QY%nbw_p18<4z zJK^gQb-zkqliThav2u85rym#`^Zo>lJmT=ATD9UWATAY{5gNZuiAI5gZaBgla7Y*M z@=pXZr=YQbL%bO z3!1f~McQxmi&sf{@z@_`6U8z6@iAM51@1Rd+OS!Q32et;q(V}Go~T(Ehgg!P8@ZRR zz=}?hT-D~jQlG{GxCo7yxw-rjlDy(pwJ`GeAQe9dlNQ1@uF!T8G9ZYktu~pKTfCn- zyP^C)*ID8ML-KY!9>5zEH#bm}Z!~ROSP1x1q#F;3o|l`2@HalVv2{UbocY@^`@jZ_ z!go~JdQVqABXcO8@rF?r`uzbU=3Yd{1=L{_BZu;&zIoMm_N_nMJEGf(^s_6S5+mcv zA=D=`8KHyC=h-Unj6`a-B?Eg`s*oW|rZ6Ex?RH;y@h5zE3wLV0*B@~qdes{${*i`@ z!rl&SPBEC&B&h$w{uaVVf3lTJGCuSEG-_u_uC0vXDu>&6yWDnlx)~JYuJ`>CGQO84 z$3E{Qoa;|5B~`TB_ybP{ZbSUUbxA>XTT2fn;+;U;?!aa|GJ`Eqw4yfKo$$n$6k_>} z&Jrl-a4RtJQT#Dvn=gnyne)q|HDUO}61H9(51^#`JTII+Ms zAe#H`h{)Jxb`THay$jOQ0g+uthz((843Ak#1Bc)#pO`)BcY`y~Lmm%J|IYbHr znguSYU}|!suS8J9_{v3iBeEx4u80OE4+nSTwb&}U2H)N4@~#)J-;iHc$#Se*HgpJ@fpHA{!j#pWrwV8x?$rRoX!*!HwHdM}HE!y`~xxH(xQEdFN$K zvTg6Vuc4B&9*4{dwDb!L{C*qx(;(4Y9{+p9KrnC7O_N?|A{C(%ajJhYpFB&>@1sW) zTv3i(cDaZeUw%B0R4AtkPUQ#f9$}bdOq9h;{c+rm=QU{&ZeC4yth!Z$y|G;#AbC=* z&m3e14qIoG7u6HfhwA;*WnN@BCxl|$7l)5F~ zPMV|as?71ZE}l`$fiCz1QG00PTX{zln{(7(L{2R3as(TOBy2YJ<1l(hS&6I379qD@ zps1>G1mnrCZ&A@6G&^%~C<77dl`;FJa?K$Y2OfoCy+(*Vw@=`D` zcwV1q;yJ%~B0SZu1k|9i2np&lgi$TJb*!3W$d9!{W5ecU+b7C;E)+&4r-VJq(Lu>9 z4!*dxsQ08}V{RT~k^;&$S7U<|##J##fVOBf(NI2vk`+6jK000wNq!0&$!k_+E!KZ+ z>F^>Yw#s)61A@qDkLP3OZd&1rO`(Zux58W?%|OouC0SnCH9@)g+A0=>umW~Hs-fO@ zs?+3_Xi~ALFw;ZPlbTU3ByNyX8au}g;Nf4`^ZXR|wJmdpvQLRvmb7pgPO7&rwz*I9 zLiaa4#LRWUh<1m59g`^oO~^T_OdKKDCBVPe9iZ)>-~Vz^^f*bzeL!ZrMuRAYZ!^lG zPY}nF-I7U|uPi>f3y1-!e%>mrVx?6k1ncWqa!Z?*p|x21gx@)g z>E64#A7QlUe!^%b^L3kK$@~mnho&HqI<$1Axt1r*l*5IZHEhJj?2qELySJHx*2@EC zRrngw>{^wl>U{|FhLy^U_98DKw-gQ!jlaGbVc#&350H#mmk}J-;B4(*Lgy_zvVuAD zB}V6rSY?od5E#L3sCjz}4+a@+R;R4?=i2&b@GfruX0iEb3N7c#p@6RmarMb}KCczS>rGKZyGT$ez5g7o@oPF5whCG$kq&Ib;S`8`w~v0OpHFJqZ}TzU`zG zE&62J!r_5mi_Q)vS8^YkKYrb<*HZV%CI62nc1G14p0w~^vx^Lt_I7F9pCW?x7#k8YJwADgeqSlw~ zcy&%bM@V$@iFkEzld0ZaBJ_{Qg}RL*d~&%zSO)N2Y?8^>G{XE&+w_uGDR`* zthC@Ni^o!TcMSkI<=}-vXZ7!qoRtI*~!rP z+SzPCw4y5^vUDoqOKXK#!^K1`)P>0NbG_1lancDnfJsc(%n9Kk;B8cmDmX1)kW!3@Lc-cN+#x7ZtW@X`J2+E8NI0&W)BawN<(W`u9qSqBQ zSO|lc-MuXDRV?veI8FMn<#r7wNxOW>U_!sH)rRx7=Vve;syX zyg4ZMB^vF@40AQd{p5Qety?31Il${C|F=SBaC2UY(Y>&X14n@@V_C5yo*s+O_{+$r z7u5HfH06o)RaL(3J`kCh`@2C^4(HCiy?JwhKOr*kgYXD%P+W(H2U*#QI$J*`OtHtpM`TvQ*8G6X6Cu$i90$Bis_ta)@cPVZ_`Vdw&1cQY z%*KR}v+T&b&Qb+@am8)^`e^->4z-`Lb9>Ft3#XlLkY5KwB=o|J*CRV7$(=1y{qRxQ z**J95>0GC-Tm2C|)A&s+RV^bjs`ZW)5R>_#BUDyO0V6XaPCb@!#MuGHT3VKKA3zd> z5iw0lfy2=81kP>U89@d!ah=2)K!z=eQW8l&o}sgNW3%xM1Ug?PlR28KZ zFz=!G?^`g~RNvGY*9K>0E%0o#vl_u82_d6qjJ%Yy=I9_Zd|7bT1oNJj6><3G>#Z^oCTtG}DAd#*$a1Hv z)+qMNfc=@=9O`&VKfLY|{lm|5-d>RkmPePbH2MGjO0>C}b_Wy2?7q{}5*FQKhkp~? zwmqurNZr5<9miKI!|A}w; zSkePRCR?7sN0>HyV5RqumL1d{j{qxWw+t`mDhNbqJ4Mb-Bcrw<;LWn&U!fL3#!=A+ ztCn%1%rec5rkbR6_r}F^z?0{i+iu6D;TQ+2s0CwQ8117`U<$Br1z&Km=il+ru>9l$ z=RVBmWqlirw1HHP^pcV?D{LexiYPWdpvjp(B{M1K**wnu#&n{lm4NqxnHdg-sGn`u z=N!PPaex-)=6&g_E*yI~4*zouqPiYQlgMH4!BUi+Vf545S-3zY+I43ReY)>w8vN>3 zPDFoqJ4Vn^z-4qbGHbp{dypHElb*1H$Jh45=iC{qb4`<711vt_({ok4vb_KX?}im^ z15K7*_R|9i+L{~m}d;-zPUE-H=1DlE5W}^s$yJmjcatb-Xa;UB#b>h!85P7$y|&iyGigW z^H1OKXJj#ny3=6?U_~grVxUWa08-;_&|Kp`?+HjpGsqZ~##qIw0uj8rE8O;g-wy91 zC_S{GPYqxQ%;9e{xHaR}PCxH#c-2l)r|RHA_PT`yKY`u zLRty2=`=-)l+Ck;m{~F}E6o+)rG0j@!li(XU6cBn7ayEhI=V0M%lkjx1%!wv67-&0 zGTx+%;b}bDQy1s~DTLsGOR|evC(x(4c!=vZNC# z6&8KC+^;9&AN{eAg;&oF>-oa!v{M$0BS_@ z221sC1%=(g@PFSIg+6R6$8;5hxBK(yQqnxKDfF3u0HdUM)Qc0Rj^Tm_Q`)yFP{k?&g?n=r)bw0*f+RvTe7&N$s39f-h3hol>x2)Y2D_H zM*J4R*P@9-BN$0M;FGGM7y&2y88muclzaIP&J7pvn$32__X3eW<`tN%^=ZM4JB--j zHgHbl^{p<5zc_%jbON3`V=(wrwCnk@Yp=Jdd*BbRRf*F14HD=$;_&SeYMRzj*etwh zasnm?89jQEfpf0fwCWNcwUlxh|0wUmDU_mXWS|J|k%|735dhE&wrEdMrp_60mSyhd z?$uo|$beB$d6eCK+7M|z>eUSCY69@w@O$dkvpOdLioIftdyVgjo?N_%K5ix|{ zDnT+%X7r{r^hHigSO#~(LWZrQ-h*p;74gd}hgHTjhlF1oO}yzsLk>)x*7vAVS6)37 z5uH3=g~4+AD3|IP!967syx(rzKGWc*2C&;LZ>d?_nL1ogZSL9Fp0xO7s}rVx1W6p7 z2n|E`RrGxz6ol7G?pADc-K?SyX@cJu)Q#C_*ww!Vo`RZ&vF?^EyIgMyUPUy&UgUk5 zc8Ne*(#~zTBp47k*np*V$$6R$se_D_OLHLT@%gCf(b0RUamZ#@HDA@`6lSb{v|;py z`t&iD@t=TQEwS?iKmb$RP63{34uz#6^u3DS8+RP*%mQ!FQ&lIPD+^fUXQDCO##u0m zIX#OEj|@q-P5}J}G5-Jr3O5e|`?parcSW_$S%s%^Ov%j$E&5)bQOD?j%)1DAN%fvK zXQUh6NQLZWlmI$Rpk*ET;LCvK8bzI{wbXY@&49x?6GA+O;>)34TJXCr#WX_P&L^DY zEtGmU{2;l~Gzy~IBE@N}WTILSpGtFBwN_;)y_j_+a z7iZCF$)6G~Z25k7{&j2d8@Myhqwv&<_3uOduPRn}hAUpA6lHzi>=LFLW0urP%y0vo zV?%Fp0?k6AnkLSUNv0haz6YW7j~;0~;skDc=?P?aigM-`qkDBNQsHH#ur~AK)*ka{%yw-2~Y2ynKs2WYL zAST2Z%$4K%LgCVR$xnB|sSnIik>hFinz!;kXCmI;znfWoL)Ei-Q6|aH%@Uow#5{l6 z-pnk@82%7V{mHa0x|B8Zok=!UR9wip7ccfQVH|J;D5M$h3Ce<%s!@=~Lq^#R{X1o0 zECudT$BR}_kMaB$?`R2G+%Ii`!b9lG`mDe9Q!;{IERVVpwfrTQa%xMRtVm?siOeSE zsZ1j4-f$<|6esqi-ac3C(tS)dLFaM{33=I7QQfoeDTU{G)&1wp8O7aMp`?W1Rf>!I z{PNQtd{=9_b6DdHY7<-1&I9(uE%&{gl%CrNmgPCWKoN%QVjSah43Ku46w~e5n1p#F zmJu|w86v;WNYqH|Oq>UnSuEq~`zlw%V9m#ZrFKZ$D8-~-IU<+fQ#Y;@A4Ixh#F28{ zyuMHNweuRp-4m`OPaPnZc#&1x^oxu_&+AoP#C6EuaPcb$^;$G7rL9C9m%q7ahnWsQ zDqBB3Bsl$tAvW`#$}vVKFJf#tn43=`XYP&En>NDSVlkKVD%?#{aQ4Mgw2bQe(68d; zCA{$90FJrabP7+kizRg=e7k;2n>6}NT)3BcHThPm`G5SvWPzS@yrS z>rj>09Ev@Sj0fy&6|aw!2rN1HCwSauLV@?^OVKmW5}tBf=KoyJL%PWN;Z~;Gub22D z=MfV6>xc29V8G6I&}>~TLVYrbqX7r<)ZmLKF_rDADES=1;#PC`XSa&>7CvJ-Atsvg zx;-C*)vt07rZuV(-ED^sKY z&i^#HE~Ij#rzP zCiX-Qe4}^cMBno_di>8)Pjka#f+8qf$aOlB$C}89Mgr04LnTHuK{4q49eLho@O4h6 zxBLFCGR?u**4^nx)BI)w#4g2qxPA_j%hJ-GYLL%q6E%gl$GuH>`PA4`(KS}`SVz0) zbvPDgpzI{&$A1;6(*rB(G;8uRnDP+aE;2D_YCJW39m%zWGT&Ao1GFoMy1*9RG;-U_ z#!?`#)<)sT;Hu{zYxMeF%=P!B;qh9T;w`oz&I_>JGF76mVFmW}Gub7i7uv!1?1G#f z@Ob^1hEPQU*Nio(+s?c2-rapYcnx-0CvPLfC5XiJlyt7p#zYbpsbm7CmI8O4e^uXo z>vp*|yJ>*e0ld=&zf+7zk$iwam4*Z7x3jz1;Qp+EdtJ}ZdZN;plb^lHG{aYm;x4#F zSoU#@;^yJfdr=cuW7S^z&WhCwUFp_%lQG?MAz3eA(;=#M2^ro2KOf{txvP1`^=>;W zQlD^4l53yy2SZiv^lLOqCfue%Y^v6r09~`Almr~F2e$7PI za+NqIdRe3U-TC5X@$l zfPqF!mJq9!Rqji?(dn*`N~LD+s*kwfs}j3Xp|JHiTOh?|)o8t%=o(ctO=AUDVYk|4 zu#L;-p7^`ad4sx_8-9Ad8gMsap$jaDM}>ImZS@ip*g5p42>B3u((Dt_b{;7r;M?|f zVT^$u*Ul02x4huwY39d(8b;;#Dn9)0{lcxmS(Uaff5X!|Olha&kP#%oaOXAxaYW>% z6_ijAOpEfD*_;%V2Bh#vvXY3dqQL9N*K|V^8sr&PbcM0d}(fFi*7!%z-c@z&G3 z@S1efAO8K5gX>5~Pn|gkWZU0=(0Lwv40lQEn+^hm1>|-cYtB)eiDsTf&MH-DL%5`Q zf_ZL;lx0br$Ufn6HD)qp$PFP6MHSp|0YST-4WU2YA&xypTn}<2y1nzVcFGDuOcUxh z?#gKTIN0?yQNb@mJ4AL9>8OHc1xl+K&~JyS+5M|7X=@m_R_m1u&}djs zPvdhT+x?j`4=d0MZ(Kd49$DB-e_XB8k*}RD5BOxt@UMvZP7^qkJIEFN{%>9f^g>s( zIWCr+xI|IV{G4Idu>SX9-eM_n!~etzvdvPMy1!4iP-GV+hB90||9 zfeOq*qQtxhN}TH$9834~lH9zWZ|{o|*9H>CEzMb`*_YbTQRKZG6Z|~hJ~_XRET2Y3 z1RF9fT^8U&@zGA>lZO&S6JoG zbr9@Uht-0W<{eFql@nd>p8qtVN?YQnEUqw62H!+#ZcZl4PDsVQjrE;{tw{RYjsNN= z8m;>6O>sp^}mb>3E5Xa z$=tqd{`}K38ix%kJ>eqcju}Fq%B8fdoGYso=^9s=7wOc8HoXHVYnnAC*+MIyFz*y< zx0~dWBfrsi#}2;4xeSnp-?Z%>=^}hN1%4M7JIDeQ_WOXQEEyIEN>;=Sg@w(_G1*4{pix3@r(U^rSRw zm=LTQ=aCmV<<-Q7X&pfTzS4~h&mi;Ew{Gv#EN-H~ikWBEotGRLUZyZ6eE}KzpdjoqU?%0~rOG<0mWpdfv zh;j;p5&LybsjBHaeu~B!_vhl+br@l1wGH^X!(H9#dObHtG zatMX$t#(CzAp>t6)9U& z0RwI+R`T*`J=|XJuJgJ@^N)3e^TdxRwxuwBHC#N9+{LxSjUZ9vpR2ER== zWnz&J>=Xe(VzRtaiOkUyq{}pAe~)QUNcUVJWE})7%4J_A(Ev+YGV6SJPEI*mV*00i z|Fwe3ij7m<_<4iz{#zxtqE2v6Ek*R(3EbaReDKd;^P<0XUkabn%x+{E_>EpQRPOTh zx}TT>2{Y@lgSZMJoXUKDt~Z{vF)seJ-*>O^5kSRBfpS={uZUB8W3Z9cDdns96eQz{ zVw8D}GZNqFJ9DJZFC_09G@AbA*A9?8l#SW*X!HF<-Xts4jNJw}Y^cz1&K zQV6lq5Rd3fOIoIXNW1?0;BV>VZ3U|a-b!6fxBYE}&5u3|kGOE!uY6QNW_Upj%7|uI zgg8i?b2>4^#x6f1@cphJUcTla&r-VmkIcyBp~4ZZQ7yzYhjmlz>Xd-Qh5%O;Xkrvq z&YHJKRuNl{3k1R+nFwg2+gT1!B%xEYX+A*>!%2bDb8b#sb-U;F#XxwA1#GJz*5kFm zaJ}wxmqcrsB~oRXoFub?!TV=Fd%H4T-Gt$q&HljyI+yYKUaZf!&%l*AEya_;xj%P3 z)-IFV*LH9h`Dw_>;f6%zlhSLSPiINXxZA=AU4!0{<@oGe>hJo7vB6ZHN1-|_2daIi zRzg&Vg8!*{M)!BmEAAfBjn}TkS&s`68jyUyOF#LofITHrBAbQFWO@iZR1$w z-pvqG$Pm8FvdLQLxY=llrjf1yHpAxpqxV=F8`;E9>!)cy)!7m}T}uHpYZ$TBLX14_d2Up_>IMX1@o`*oVw zm`r(8Go!D3Y^a`w(&Er{jGXj8y$s!}xib$lYqojNx9qsE7ihRYqNBDziO3kzH0tj+ zNL0Ao2qgy|vxs-@G@chK`X0bZ^$Cd2sxd}&mlwFg)$OwbORWXix?qnYgS`d@@Eqr{ zjTa2PPe$v;e_lw^3Idqs5;~)8&X_8CtA0#GIIpb&A-QurktUEs>!j65_MB-7$oVH` zMP?l#j_`0UQkZ@2^i_@4An!u=oF2Kk8P?;XCh3(`G#9!UMctbGvfdL2CdQ~aJS4~dnHX9kz# z(#y^9o>dhi+yL1iI!U)|y-`^iT**GJo579`jLHY9)4by(c1lvdO?;Etm5arf2@>a5 zre?VMj~{7k4rIuSMb`at5uy;B8461U=3l-lTWR50YkWCu9}IWtSDyU&uI(C(PwhN5 z7T~DU<9N3um8g|RDa(^2;EXgwa0!T@v3|q4M4}pb!a!}>1uPYLXpfrEBcrY0lBNhf z@O1p10w;qhqz+26a0hW^Sr2M zS%aFahoKo%S!+)Yra4B9CWy<}w1Szdkf#*0TiT;?Da~i${lg^qzO4hLqW&u2*t}fo z{R$(sQ0A;Bz)qVv7@V55nu(OarU<#+6jw^Md}GdS4c@x#T6kF zc<{oEbnd;_@yw}110B5!z40!7RKFv6DSl&h7ZokO6oP_sB{Z^v6_wxWc6eNVC3*~x zp-Q_bbexuaxI_rBhpk8)9lLW*RLr%n!%ru1Z}PN{5*}IHmltL_aWL3CNdLMg_M=ux z@a+d}5}~>SWciNs6gz9HOC>q?;&Nc_e;AI90drI&V7d$X;Z}nT9boR`o=_@gv@<}F z@-)OsR9?2J^OR`TUIUOKnK^WGvFu!AIt*$cxu%lf?r@;U4_BW`Vc@OPJK22!t|~4W z(Jkag3HU+wJ9^?X8^=xVIN7x%@EAHOCrBR>!ixx*4isd;K*o6ZpU#9Anf_v&q0hGGQ(J4da52o^fU}`pcw@XIe~<@%BzECh|jT{^i`| zh`cy$?GV^lKYXEX-qvk%)W#`>LCI-T6a}>Q+-NoPjWw>4 z0lIbjjI04d{{1edGuqoUW-;r*n5H)2p(g$k{_39^rNW_ht>&C3+45<<8e>SBz?C0R zOG{2MFV}f*YBNm`HApW=aK@YCiYY;=%X{2ks4)s5NFp;enDmq&>3i9?p>7fBtHQdP zTXJKQQr5AjYrvkM)qpE#5Al>;+tY*oG=JKQJ5h-oXZNd&Vw|m4@ zn--@(++hb+B k8YCaQ1sl7JB@oLbNV+hBD=nfWwl1+u$>N;I_o$y%--GvKy1k(< zydmzGp2(rWV~)uvqMAso3vi{Is$61oJ>FU8isX&YxbR!hnr9d57K*|eX~*F%@4-P_ z3C}TRjd|wJx5`*Z-y>5FHHJYb5sL>E5e7@&szV3TfXcGMH2nF%_-eX;fR`-Ag&51w zlqDt}`B*Fwv2RUbuf<98@P^{{?L)6h7Cvu85pr0PU$&1buFM=)3U3}NW(6VG-C^2w z+IKHRDTIaE)E-PZ*!NLPh;88k6T*KcIbGo>*`4i&`9I6_WEF|T&ks3d*Dc-%bF?f& zTohA|U$d<$&EIGG+L5E;n|Fy#|q| zNOE}FvOswtEa?b(1FxL)CNx(ZWj;!hD+x=z*)qdGzuPZ{D zHazz&i}<6cqp5iEqtUmU1^?k~bG(0Hr0}=(f7EO?x9K*HKKui0T{B(N@|QecFOf<1 z|G+buymk{u{Fk^tuA0PMV_f+4$AK%Gj+_5g;XjDt9=vw@M`axx$kLT^VO0x^zf@GX z-WWdm-&x6rb=v>gFf8=Yvs*$@)@b@$GjWvn{y&6pcc~q{?UY3LQTcyPd)d0D`cTg2 zAJv)7C4W({cq61-S!cbUG5=AvW?{7NM?3!qs+BATzTz9_`+w&CBg`(ppg+X`!*YfY(B)A^4De~4X)&IBue7ao1l+durn0<8V3?*EYSZz2A3QwQNw zoYIFp{GV>21=T(B^f9x7n>WPISJM8AB=c{7|DRT=4pQ1@d+=;!%1F*=QipA+UZh-7 z2SFkt^Z#*6&5c8a$9)H}b=e_T8`4{08Q@cHN2-Q%`%<-ZXgU>@O<* z1%nL?A>aDnVuXIvJqkG$@b2fbpCoVpddW+u-;p!PB(u3Ho6)X``xi~79H;J|eHtm^ zcIu{-eC3rN552Wp_`HWRcTd>{-_bp8o&SB&_u?9LBzAZBslO?63^wG3?e;5HUMznc zmu1oW*CYNcs2{xl{*igX%_3^0-^CXuz(?lL__%SkfGnrXEvJZ^O7!M__7Ce&U$?Ba3z@;<7WyV{6Q)1=7tyQhN8 z+~N5@3opDS`a}0xq3#EMF&FB7e^<(b(tjP*LS6L#tMuXe-J+YpxBt>)UigBY`!;z0 zS|RnF-^HWWz=$6*3R^}fqs$K(EiV1|=fBzK=*pe5|CUe?e(B5)g@xA$^{D*IhJWc% zZbLFFI82pX{vo62KJ{mj!q)qjjCyyT{onSv-n=oha4!U%Y&`zVDgss2um+ig`PTZdy zEz@6=TYmA^p(6k6V1M`0N{{lk|8$7|?nbk&+5gik{#{s|UUooe(SJIJAHQyE+yB!; s{e4Y`Rq+|0fBJ-f{kl`_e2fo1oO|i8(;Eq-i11^1)#gf-$=%rh132H5mjD0& diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj b/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj index aa885b9071..d2fbef25a0 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj @@ -1,44 +1,40 @@ - - - + + + - + + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} + PowerToys.MouseJump.Common.UnitTests + PowerToys.MouseJump.Common.UnitTests + PowerToys MouseJump.Common.UnitTests + Library + ..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJump.Common.UnitTests\ + false + false - {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} - PowerToys.MouseJump.Common.UnitTests - PowerToys.MouseJump.Common.UnitTests - PowerToys MouseJump.Common.UnitTests - Exe - $(RepoRoot)$(Platform)\$(Configuration)\tests\MouseJump.Common.UnitTests\ - false - false + enable + enable + - enable - true - enable + + + + + + - + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/ConfigHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/ColorHelper.cs similarity index 96% rename from src/modules/MouseUtils/MouseJump.Common/Helpers/ConfigHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/ColorHelper.cs index bc2e67203d..f97dcbeebe 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/ConfigHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/ColorHelper.cs @@ -1,12 +1,13 @@ -// Copyright (c) Microsoft Corporation +// 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 System.Drawing; using System.Globalization; namespace MouseJump.Common.Helpers; -public static class ConfigHelper +public static class ColorHelper { public static Color? ToUnnamedColor(Color? value) { diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/DeviceHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/DeviceHelper.cs new file mode 100644 index 0000000000..87b7598e77 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/DeviceHelper.cs @@ -0,0 +1,37 @@ +// 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 MouseJump.Models.Display; +using MouseJump.Models.Drawing; + +namespace MouseJump.Common.Helpers; + +public static class DeviceHelper +{ + public static DisplayInfo GetDisplayInfo() + { + var devices = new List(); + + // add the local devices + devices.Add( + new( + hostname: Environment.MachineName, + localhost: true, + screens: ScreenHelper.GetAllScreens())); + + return new DisplayInfo( + devices: devices); + } + + public static ScreenInfo GetActivatedScreen(DeviceInfo deviceInfo, PointInfo activatedLocation) + { + ArgumentNullException.ThrowIfNull(deviceInfo); + ArgumentNullException.ThrowIfNull(activatedLocation); + + var activatedScreen = deviceInfo.Screens + .Single(screen => screen.DisplayArea.Contains(activatedLocation)); + + return activatedScreen; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/DpiModeHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/DpiModeHelper.cs new file mode 100644 index 0000000000..1aec6401b8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/DpiModeHelper.cs @@ -0,0 +1,55 @@ +// 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 System.Runtime.Versioning; + +using Windows.Win32; +using Windows.Win32.UI.HiDpi; + +namespace MouseJump.Common.Helpers; + +///

+/// Based on WinForms Application.HighDpiMode +/// see https://github.com/dotnet/winforms/blob/1c324d074280ab5de6342d973069faa687f2c165/src/System.Windows.Forms/System/Windows/Forms/Application.cs#L18 +/// https://github.com/dotnet/winforms/blob/1c324d074280ab5de6342d973069faa687f2c165/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs#L14 +/// +public static class DpiModeHelper +{ + /// + /// See https://github.com/dotnet/winforms/blob/bd91bfb26ce90ac31e950c01dcb2b6e0776453a7/src/System.Private.Windows.Core/src/System/Private/Windows/OsVersion.cs#L9 + /// + private static bool IsWindows10_1607OrGreater() + => OperatingSystem.IsWindowsVersionAtLeast(major: 10, build: 14393); + + /// + /// Ensures that the application is running in per-monitor v2 DPI awareness mode. + /// + /// + /// See https://github.com/dotnet/winforms/blob/bd91bfb26ce90ac31e950c01dcb2b6e0776453a7/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs#L368 + /// + [SupportedOSPlatform("windows10.0.14393")] + public static void EnsurePerMonitorV2Enabled() + { + // PowerToys supports the following operating systems: + // + // * Windows 11 or Windows 10 version 2004 (code name 20H1 / build number 19041) or newer. + // + // so we'll do the same(ish) + if (!DpiModeHelper.IsWindows10_1607OrGreater()) + { + throw new PlatformNotSupportedException( + "Windows 10 version1607 or higher is required to use this application."); + } + + // there's a weird problem where AreDpiAwarenessContextsEqual was returning TRUE in debug mode + // but FALSE in release mode and i couldn't work out why, so we can't do it the *right* way. + // we'll just use GetAwarenessFromDpiAwarenessContext instead as a near-enough workaround. + var dpiAwarenessContext = PInvoke.GetThreadDpiAwarenessContext(); + var dpiAwareness = PInvoke.GetAwarenessFromDpiAwarenessContext(dpiAwarenessContext); + if (dpiAwareness != DPI_AWARENESS.DPI_AWARENESS_PER_MONITOR_AWARE) + { + throw new InvalidOperationException($"high dpi mode is not set to {nameof(DPI_AWARENESS.DPI_AWARENESS_PER_MONITOR_AWARE)}"); + } + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs index 668e90dd3d..99eef641ac 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs @@ -3,62 +3,101 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using MouseJump.Common.Imaging; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Layout; -using MouseJump.Common.Models.Styles; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; +using MouseJump.Models.ViewModel; namespace MouseJump.Common.Helpers; public static class DrawingHelper { - public static Bitmap RenderPreview( - PreviewLayout previewLayout, - IImageRegionCopyService imageCopyService, - Action? previewImageCreatedCallback = null, - Action? previewImageUpdatedCallback = null) + /// + /// Renders a preview image of the specified canvas layout. + /// + /// + /// The layout of the canvas, including the layout of all devices and screens. + /// + /// + /// The screen that is currently activated (i.e. the one that the user is interacting with). + /// + /// + /// A list of IImageRegionCopyService implementations, one for each device in the canvas layout. + /// + /// + /// A callback that is invoked when the preview image is created. + /// + /// + /// A callback that is invoked when the preview image is updated. + /// + /// + /// A preview image of the canvas layout. + /// + public static async Task RenderPreviewAsync( + CanvasViewModel canvasLayout, + ScreenInfo activatedScreen, + List imageRegionCopyServices, + Func? previewImageCreatedCallback = null, + Func? previewImageUpdatedCallback = null) { var stopwatch = Stopwatch.StartNew(); // initialize the preview image - var previewBounds = previewLayout.PreviewBounds.OuterBounds.ToRectangle(); + var previewBounds = canvasLayout.CanvasBounds.OuterBounds.ToRectangle(); var previewImage = new Bitmap(previewBounds.Width, previewBounds.Height, PixelFormat.Format32bppPArgb); - var previewGraphics = Graphics.FromImage(previewImage); - previewImageCreatedCallback?.Invoke(previewImage); + using var previewGraphics = Graphics.FromImage(previewImage); + if (previewImageCreatedCallback != null) + { + await previewImageCreatedCallback(previewImage); + } - DrawingHelper.DrawRaisedBorder(previewGraphics, previewLayout.PreviewStyle.CanvasStyle, previewLayout.PreviewBounds); + DrawingHelper.DrawRaisedBorder(previewGraphics, canvasLayout.CanvasBounds, canvasLayout.CanvasStyle); DrawingHelper.DrawBackgroundFill( previewGraphics, - previewLayout.PreviewStyle.CanvasStyle, - previewLayout.PreviewBounds, + canvasLayout.CanvasStyle, + canvasLayout.CanvasBounds, []); // sort the source and target screen areas into the order we want to // draw them, putting the activated screen first (we need to capture // and draw the activated screen before we show the form because // otherwise we'll capture the form as part of the screenshot!) - var sourceScreens = new List { previewLayout.Screens[previewLayout.ActivatedScreenIndex] } - .Concat(previewLayout.Screens.Where((_, idx) => idx != previewLayout.ActivatedScreenIndex)) - .ToList(); - var targetScreens = new List { previewLayout.ScreenshotBounds[previewLayout.ActivatedScreenIndex] } - .Concat(previewLayout.ScreenshotBounds.Where((_, idx) => idx != previewLayout.ActivatedScreenIndex)) + var screenDrawingOps = canvasLayout.DeviceLayouts + .SelectMany( + (deviceLayout, deviceIndex) => deviceLayout.ScreenLayouts.Select( + screenLayout => new + { + DeviceIndex = deviceIndex, + DeviceLayout = deviceLayout, + ScreenLayout = screenLayout, + CopyService = imageRegionCopyServices[deviceIndex], + })) + .OrderByDescending( + pair => object.ReferenceEquals(pair.ScreenLayout.ScreenInfo, activatedScreen)) .ToList(); // draw all the screenshot bezels - foreach (var screenshotBounds in previewLayout.ScreenshotBounds) + foreach (var screenDrawingOp in screenDrawingOps) { DrawingHelper.DrawRaisedBorder( - previewGraphics, previewLayout.PreviewStyle.ScreenStyle, screenshotBounds); + previewGraphics, screenDrawingOp.ScreenLayout.ScreenBounds, screenDrawingOp.ScreenLayout.ScreenStyle); } var refreshRequired = false; var placeholdersDrawn = false; - for (var i = 0; i < sourceScreens.Count; i++) + for (var i = 0; i < screenDrawingOps.Count; i++) { - imageCopyService.CopyImageRegion(previewGraphics, sourceScreens[i], targetScreens[i].ContentBounds); + var screenDrawingOp = screenDrawingOps[i]; + + screenDrawingOp.CopyService.CopyImageRegion( + targetGraphics: previewGraphics, + sourceBounds: screenDrawingOp.ScreenLayout.ScreenInfo.DisplayArea, + targetBounds: screenDrawingOp.ScreenLayout.ScreenBounds.ContentBounds); refreshRequired = true; // show the placeholder images and show the form if it looks like it might take @@ -70,19 +109,26 @@ public static class DrawingHelper { DrawingHelper.DrawScreenPlaceholders( previewGraphics, - previewLayout.PreviewStyle.ScreenStyle, - targetScreens.GetRange(i + 1, targetScreens.Count - i - 1)); + screenDrawingOp.ScreenLayout.ScreenStyle, + screenDrawingOps + .Skip(i + 1) + .Select(drawingOp => drawingOp.ScreenLayout.ScreenBounds) + .ToList()); placeholdersDrawn = true; } - previewImageUpdatedCallback?.Invoke(); + if (previewImageUpdatedCallback is not null) + { + await previewImageUpdatedCallback(previewImage); + } + refreshRequired = false; } } - if (refreshRequired) + if (refreshRequired && (previewImageUpdatedCallback is not null)) { - previewImageUpdatedCallback?.Invoke(); + await previewImageUpdatedCallback(previewImage); } stopwatch.Stop(); @@ -94,10 +140,10 @@ public static class DrawingHelper /// Draws a border shape with an optional raised 3d highlight and shadow effect. /// private static void DrawRaisedBorder( - Graphics graphics, BoxStyle boxStyle, BoxBounds boxBounds) + Graphics graphics, BoxBounds boxBounds, BoxStyle boxStyle) { var borderStyle = boxStyle.BorderStyle; - if ((borderStyle.Horizontal == 0) || (borderStyle.Vertical == 0)) + if ((borderStyle.Horizontal < 1) || (borderStyle.Vertical < 1)) { return; } @@ -109,7 +155,7 @@ public static class DrawingHelper // draw the main box border using var borderBrush = new SolidBrush(borderStyle.Color.Value); - var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle()); + using var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle()); borderRegion.Exclude(boxBounds.PaddingBounds.ToRectangle()); graphics.FillRegion(borderBrush, borderRegion); @@ -195,7 +241,7 @@ public static class DrawingHelper // it's faster to build a region with the screen areas excluded // and fill that than it is to fill the entire bounding rectangle - var backgroundRegion = new Region(backgroundBounds.ToRectangle()); + using var backgroundRegion = new Region(backgroundBounds.ToRectangle()); foreach (var exclude in excludeBounds) { backgroundRegion.Exclude(exclude.ToRectangle()); @@ -208,14 +254,14 @@ public static class DrawingHelper /// Draws placeholder background images for the specified screens on the preview. /// private static void DrawScreenPlaceholders( - Graphics graphics, BoxStyle screenStyle, IList screenBounds) + Graphics graphics, BoxStyle screenStyle, List screenBounds) { if (screenBounds.Count == 0) { return; } - if (screenStyle?.BackgroundStyle?.Color1 == null) + if (screenStyle.BackgroundStyle.Color1 == null) { return; } diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs index 589ff42178..88f219ac29 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs @@ -2,152 +2,401 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Layout; -using MouseJump.Common.Models.Styles; +using System.Diagnostics; +using System.Drawing; + +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; +using MouseJump.Models.ViewModel; namespace MouseJump.Common.Helpers; public static class LayoutHelper { - public static PreviewLayout GetPreviewLayout( - PreviewStyle previewStyle, List screens, PointInfo activatedLocation) + public static RectangleInfo GetCombinedScreenBounds(List screenBounds) + { + return screenBounds.Skip(1).Aggregate( + seed: screenBounds.First(), + (combined, screenBounds) => combined.Union(screenBounds)); + } + + public static FormViewModel GetFormLayout( + PreviewStyle previewStyle, DisplayInfo displayInfo, ScreenInfo activatedScreen, PointInfo activatedLocation) { ArgumentNullException.ThrowIfNull(previewStyle); - ArgumentNullException.ThrowIfNull(screens); + ArgumentNullException.ThrowIfNull(displayInfo); + ArgumentNullException.ThrowIfNull(activatedScreen); + ArgumentNullException.ThrowIfNull(activatedLocation); - if (screens.Count == 0) + /* + + example layout: + + +-------------------------------[form]------------------------------+ + |▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒[canvas]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| + |▒▒+----------------------------[grid]----+----------------------+▒▒| + |▒▒| |▓▓▓▓▓▓[device 2]▓▓▓▓▓▓|▒▒| + |▒▒| |▓▓░░░░[screen 1]░░░░▓▓|▒▒| + |▒▒|▓▓▓▓▓▓▓▓▓▓▓▓▓▓[device 1]▓▓▓▓▓▓▓▓▓▓▓▓▓▓|▓▓░░ ░░▓▓|▒▒| + |▒▒|▓▓░░░░[screen 1]░░░░░░[screen 2]░░░░▓▓|▓▓░░ ░░▓▓|▒▒| + |▒▒|▓▓░░ ░░ ░░▓▓|▓▓░░ ░░▓▓|▒▒| + |▒▒|▓▓░░ ░░ ░░▓▓|▓▓░░░░░░░░░░░░░░░░░░▓▓|▒▒| + |▒▒|▓▓░░ ░░ ░░▓▓|▓▓░░░░[screen 2]░░░░▓▓|▒▒| + |▒▒|▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓|▓▓░░ ░░▓▓|▒▒| + |▒▒|▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓|▓▓░░ ░░▓▓|▒▒| + |▒▒| |▓▓░░ ░░▓▓|▒▒| + |▒▒| |▓▓░░░░░░░░░░░░░░░░░░▓▓|▒▒| + |▒▒| |▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓|▒▒| + |▒▒+--------------------------------------+----------------------+▒▒| + |▒▒| ▓▓▓▓▓▓[device 3]▓▓▓▓▓▓ | |▒▒| + |▒▒| ▓▓░░░░[screen 1]░░░░▓▓ | |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | ▓▓▓[device 4]▓▓▓ |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | ▓▓░[screen 1]░▓▓ |▒▒| + |▒▒| ▓▓░░░░░░░░░░░░░░░░░░▓▓ | ▓▓░░ ░░▓▓ |▒▒| + |▒▒| ▓▓░░░░[screen 2]░░░░▓▓ | ▓▓░░ ░░▓▓ |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | ▓▓░░░░░░░░░░░░▓▓ |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ |▒▒| + |▒▒| ▓▓░░ ░░▓▓ | |▒▒| + |▒▒| ▓▓░░░░░░░░░░░░░░░░░░▓▓ | |▒▒| + |▒▒| ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ | |▒▒| + |▒▒+--------------------------------------+----------------------+▒▒| + |▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| + +-------------------------------------------------------------------+ + + */ + + // arrange the form, canvas, devices and screens + var formLayout = LayoutHelper.CreateInitialFormLayout(previewStyle, displayInfo, activatedScreen); + LayoutHelper.ArrangeAndScaleDeviceLayouts(formLayout); + LayoutHelper.ArrangeAndScaleScreenLayouts(formLayout); + LayoutHelper.ArrangeAndResizeCanvasLayout(formLayout); + LayoutHelper.ArrangeAndResizeFormLayout(formLayout, activatedScreen, activatedLocation); + + return formLayout.Build(); + } + + internal static FormViewModel.Builder CreateInitialFormLayout( + PreviewStyle previewStyle, DisplayInfo displayInfo, ScreenInfo activatedScreen) + { + ArgumentNullException.ThrowIfNull(previewStyle); + ArgumentNullException.ThrowIfNull(displayInfo); + + // check we have at least one device + if (displayInfo.Devices.Count == 0) { - throw new ArgumentException("Value must contain at least one item.", nameof(screens)); + throw new ArgumentException("Value must contain at least one device.", nameof(displayInfo)); } - var builder = new PreviewLayout.Builder(); - builder.Screens = screens.ToList(); - - // calculate the bounding rectangle for the virtual screen - builder.VirtualScreen = LayoutHelper.GetCombinedScreenBounds(builder.Screens); - - // find the screen that contains the activated location - this is the - // one we'll show the preview form on - var activatedScreen = builder.Screens.Single( - screen => screen.Contains(activatedLocation)); - builder.ActivatedScreenIndex = builder.Screens.IndexOf(activatedScreen); + /* + // check each device has at least one screen. + for (var deviceIndex = 0; deviceIndex < displayInfo.Devices.Count; deviceIndex++) + { + var device = displayInfo.Devices[deviceIndex]; + if (device.Screens.Count == 0) + { + throw new ArgumentException($"{nameof(displayInfo)}.{nameof(displayInfo.Devices)}[{deviceIndex}] must contain at least one screen.", nameof(displayInfo)); + } + } + */ // work out the maximum allowed size of the preview form: // * can't be bigger than the activated screen // * can't be bigger than the configured canvas size - var maxPreviewSize = activatedScreen.Size - .Intersect(previewStyle.CanvasSize); + var formMaxBounds = new RectangleInfo( + previewStyle.CanvasSize + .Clamp(activatedScreen.DisplayArea.Size)); - // the "content area" (i.e. drawing area) for screenshots is inside the - // preview border and inside the preview padding (if any) - var maxContentSize = maxPreviewSize - .Shrink(previewStyle.CanvasStyle.MarginStyle) - .Shrink(previewStyle.CanvasStyle.BorderStyle) - .Shrink(previewStyle.CanvasStyle.PaddingStyle); + var screenStyles = LayoutHelper.GetDeviceScreenStyles(previewStyle, displayInfo.Devices.Count).ToList(); - // work out the actual size of the "content area" by scaling the virtual screen - // to fit inside the maximum content area while maintaining its aspect ration. - // we'll also offset it to allow for any margins, borders and padding - var contentBounds = builder.VirtualScreen.Size - .ScaleToFit(maxContentSize, out var scalingRatio) - .Round() - .Clamp(maxContentSize) - .PlaceAt(0, 0) - .Offset(previewStyle.CanvasStyle.MarginStyle.Left, previewStyle.CanvasStyle.MarginStyle.Top) - .Offset(previewStyle.CanvasStyle.BorderStyle.Left, previewStyle.CanvasStyle.BorderStyle.Top) - .Offset(previewStyle.CanvasStyle.PaddingStyle.Left, previewStyle.CanvasStyle.PaddingStyle.Top); + // create an initial form layout. + // this is a nested structure of mutable "builder" objects that can be used to + // build the final immutable layout objects once all the bounds have been calculated + var formLayout = new FormViewModel.Builder + { + FormBounds = RectangleInfo.Empty, + CanvasLayout = new() + { + CanvasBounds = BoxBounds.CreateFromOuterBounds( + outerBounds: formMaxBounds, + boxStyle: previewStyle.CanvasStyle), + CanvasStyle = previewStyle.CanvasStyle, + DeviceLayouts = displayInfo.Devices.Select( + (deviceInfo, deviceIndex) => new DeviceViewModel.Builder + { + DeviceInfo = deviceInfo, + DeviceBounds = BoxBounds.Empty, + DeviceStyle = BoxStyle.Empty, + ScreenLayouts = deviceInfo.Screens.Select( + screenInfo => new ScreenViewModel.Builder + { + ScreenInfo = screenInfo, + ScreenBounds = BoxBounds.Empty, + ScreenStyle = screenStyles[deviceIndex], + }).ToList(), + }).ToList(), + }, + }; - // now we know the actual size of the content area we can work outwards to - // get the size of the background bounds including margins, borders and padding - builder.PreviewStyle = previewStyle; - builder.PreviewBounds = LayoutHelper.GetBoxBoundsFromContentBounds( - contentBounds, - previewStyle.CanvasStyle); + return formLayout; + } - // ... and then the size and position of the preview form on the activated screen - // * center the form to the activated position, but nudge it back - // inside the visible area of the activated screen if it falls outside - var formBounds = builder.PreviewBounds.OuterBounds - .Center(activatedLocation) - .Clamp(activatedScreen); - builder.FormBounds = formBounds; + internal static IEnumerable GetDeviceScreenStyles(PreviewStyle previewStyle, int screenCount) + { + // work out the colors to use for the screen borders for each device + // (add the default localhost color first and then loop over the extra colors until we've got enough) + var screenBorderColors = new List { previewStyle.ScreenStyle.BorderStyle.Color }; + while (screenBorderColors.Count < screenCount) + { + if (previewStyle.ExtraColors?.Count > 0) + { + screenBorderColors.AddRange(previewStyle.ExtraColors.Cast()); + continue; + } - // now calculate the positions of each of the screenshot images on the preview - builder.ScreenshotBounds = builder.Screens + screenBorderColors.Add(previewStyle.ScreenStyle.BorderStyle.Color); + } + + // convert the colors into screen styles + // note - only create a new ScreenStyle if the border color is different. + // (this is to preserve a reference to ScreenStyle.Empty - and + // "ScreenStyle.IsEmpty == true" - if the new border color is Transparent) + return screenBorderColors + .Take(screenCount) .Select( - screen => LayoutHelper.GetBoxBoundsFromOuterBounds( - screen - .Offset(builder.VirtualScreen.Location.ToSize().Invert()) - .Scale(scalingRatio) - .Offset(builder.PreviewBounds.ContentBounds.Location.ToSize()) - .Round(), - previewStyle.ScreenStyle)) - .ToList(); - - return builder.Build(); - } - - public static RectangleInfo GetCombinedScreenBounds(List screens) - { - return screens.Skip(1).Aggregate( - seed: screens.First(), - (bounds, screen) => bounds.Union(screen)); + color => previewStyle.ScreenStyle.BorderStyle.Color == color + ? previewStyle.ScreenStyle + : new BoxStyle( + previewStyle.ScreenStyle.MarginStyle, + previewStyle.ScreenStyle.BorderStyle.WithColor(color), + previewStyle.ScreenStyle.PaddingStyle, + previewStyle.ScreenStyle.BackgroundStyle)); } /// - /// Calculates the bounds of the various areas of a box, given the content bounds and the box style. - /// Starts with the content bounds and works outward, enlarging the content bounds by the padding, border, and margin sizes to calculate the outer bounds of the box. + /// Arranges the device layouts into a non-overlapping grid and scales them to fit inside the specified content bounds. /// - /// The content bounds of the box. - /// The style of the box, which includes the sizes of the margin, border, and padding areas. - /// A object that represents the bounds of the different areas of the box. - /// Thrown when or is null. - /// Thrown when any of the styles in is null. - public static BoxBounds GetBoxBoundsFromContentBounds( - RectangleInfo contentBounds, - BoxStyle boxStyle) + internal static void ArrangeAndScaleDeviceLayouts(FormViewModel.Builder formLayout) { - ArgumentNullException.ThrowIfNull(contentBounds); - ArgumentNullException.ThrowIfNull(boxStyle); - if (boxStyle.PaddingStyle == null || boxStyle.BorderStyle == null || boxStyle.MarginStyle == null) + var deviceLayouts = formLayout.CanvasLayout?.DeviceLayouts + ?? throw new InvalidOperationException(); + var contentBounds = formLayout.CanvasLayout.CanvasBounds?.ContentBounds + ?? throw new InvalidOperationException(); + + // build an initial grid of devices at 100% scale. the grid is currently a single row + // of cells high with all the devices arranged left to right for the time being. + // we'll enhance this to allow for multiple rows later to support the + // Mouse Without Borders "square" arrangement. + var gridRowCount = 1; + var gridColumnCount = deviceLayouts.Count; + var deviceGrid = new DeviceViewModel.Builder[gridRowCount, gridColumnCount]; + for (var columnIndex = 0; columnIndex < gridColumnCount; columnIndex++) { - throw new ArgumentException(null, nameof(boxStyle)); + deviceGrid[0, columnIndex] = deviceLayouts[columnIndex]; } - var paddingBounds = contentBounds.Enlarge(boxStyle.PaddingStyle); - var borderBounds = paddingBounds.Enlarge(boxStyle.BorderStyle); - var marginBounds = borderBounds.Enlarge(boxStyle.MarginStyle); - var outerBounds = marginBounds; - return new( - outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds); + // if a device has zero screens (or if we failed to connect and read screen layouts) we + // still want to allocate space in the full size grid so that it doesn't disappear into + // a zero width or height cell. + var fullSizeGridCellMinSize = new Size(1024, 768); + + // find the size of the tallest device in each row and the widest device in each column. + // this tells us how tall each row needs to be and how wide each column needs to be. + var fullSizeGridCellBounds = new RectangleInfo[gridRowCount, gridColumnCount]; + var fullSizeRowHeights = new decimal[gridRowCount]; + var fullSizeColumnWidths = new decimal[gridColumnCount]; + for (var rowIndex = 0; rowIndex < gridRowCount; rowIndex++) + { + for (var columnIndex = 0; columnIndex < gridColumnCount; columnIndex++) + { + var deviceInfo = deviceGrid[rowIndex, columnIndex].DeviceInfo ?? throw new InvalidOperationException(); + var deviceBounds = deviceInfo.GetCombinedDisplayArea(); + + // if the device bounds are empty, it probably means there aren't any + // screens available for this device so we'll use the minimum size instead + // for the grid cell size + var fullSizeGridCell = deviceBounds.IsEmpty + ? deviceBounds.Resize( + width: fullSizeGridCellMinSize.Width, + height: fullSizeGridCellMinSize.Height) + : deviceBounds; + + fullSizeGridCellBounds[rowIndex, columnIndex] = fullSizeGridCell; + fullSizeRowHeights[rowIndex] = Math.Max(fullSizeRowHeights[rowIndex], fullSizeGridCell.Height); + fullSizeColumnWidths[columnIndex] = Math.Max(fullSizeColumnWidths[columnIndex], fullSizeGridCell.Width); + } + } + + // use the row heights to calculate the absolute coordinates of each grid row + var fullSizeRowCoordinates = new decimal[gridRowCount]; + fullSizeRowCoordinates[0] = 0; + for (var rowIndex = 1; rowIndex < gridRowCount; rowIndex++) + { + fullSizeRowCoordinates[rowIndex] = fullSizeRowCoordinates[rowIndex - 1] + fullSizeRowHeights[rowIndex - 1]; + } + + // use the column widths to calculate the absolute coordinates of each grid column + var fullSizeColumnCoordinates = new decimal[gridColumnCount]; + fullSizeColumnCoordinates[0] = 0; + for (var columnIndex = 1; columnIndex < gridColumnCount; columnIndex++) + { + fullSizeColumnCoordinates[columnIndex] = fullSizeColumnCoordinates[columnIndex - 1] + fullSizeColumnWidths[columnIndex - 1]; + } + + // calculate the full size bounds for the entire grid + var fullSizeGridBounds = new RectangleInfo( + x: 0, + y: 0, + width: fullSizeColumnCoordinates[gridColumnCount - 1] + fullSizeColumnWidths[gridColumnCount - 1], + height: fullSizeRowCoordinates[gridRowCount - 1] + fullSizeRowHeights[gridRowCount - 1]); + + // work out the scaling factor that will fit the full size grid into the content bounds + var scaledGridSize = fullSizeGridBounds.Size + .ScaleToFit(contentBounds.Size, out var scalingFactor); + + // scale the row and column coordinates by the factor. round coordinates to ensure they + // don't overlap when rendered as pixel dimensions in the image. we'll also add a final + // "fencepost" coordinate to simplify building the end cell of each row and column in the + // scaled grid. + var scaledRowCoordinates = fullSizeRowCoordinates + .Select(fullSizeRowCoordinate => Math.Round(fullSizeRowCoordinate * scalingFactor)) + .Concat([scaledGridSize.Height]) + .Select(scaledRowCoordinate => scaledRowCoordinate + contentBounds.Top) + .ToArray(); + var scaledColumnCoordinates = fullSizeColumnCoordinates + .Select(fullSizeColumnCoordinate => Math.Round(fullSizeColumnCoordinate * scalingFactor)) + .Concat([scaledGridSize.Width]) + .Select(scaledColumnCoordinate => scaledColumnCoordinate + contentBounds.Left) + .ToArray(); + + // arrange the device layouts into their scaled grid positions + for (var rowIndex = 0; rowIndex < gridRowCount; rowIndex++) + { + for (var columnIndex = 0; columnIndex < gridColumnCount; columnIndex++) + { + var fullSizeCellSize = fullSizeGridCellBounds[rowIndex, columnIndex].Size; + if ((fullSizeCellSize.Width == 0) || (fullSizeCellSize.Height == 0)) + { + // if the full size bounds are zero width or height, it probably means there aren't any + // screens available for this device so we can't scale it to fit inside the grid cell + continue; + } + + // work out the scaled coordinates for this grid cell + var scaledGridCellBounds = new RectangleInfo( + x: scaledColumnCoordinates[columnIndex], + y: scaledRowCoordinates[rowIndex], + width: scaledColumnCoordinates[columnIndex + 1] - scaledColumnCoordinates[columnIndex], + height: scaledRowCoordinates[rowIndex + 1] - scaledRowCoordinates[rowIndex]); + + // scale and center the device layout to fit inside the grid cell + var deviceLayout = deviceGrid[rowIndex, columnIndex]; + deviceLayout.DeviceBounds = BoxBounds.CreateFromOuterBounds( + outerBounds: new RectangleInfo( + size: fullSizeCellSize.ScaleToFit(scaledGridCellBounds.Size)) + .Center(scaledGridCellBounds.Midpoint) + .Round() + .Intersect(scaledGridCellBounds.Round()), + boxStyle: deviceLayout.DeviceStyle); + } + } + + // verify that we're pixel-perfect on the positions + var scaledDeviceBounds = RectangleInfo.Union( + deviceLayouts + .Select(deviceLayout => deviceLayout.DeviceBounds.OuterBounds)); + Debug.Assert( + (scaledDeviceBounds.Width == contentBounds.Width) || (scaledDeviceBounds.Height == contentBounds.Height), + string.Join( + "\r\n", + $"{nameof(ArrangeAndScaleDeviceLayouts)} - scaled device layout does not perfectly fill content bounds:", + $"scaled grid = '{scaledDeviceBounds}'", + $"content bounds = '{contentBounds}'")); } /// - /// Calculates the bounds of the various areas of a box, given the outer bounds and the box style. - /// This method starts with the outer bounds and works inward, shrinking the outer bounds by the margin, border, and padding sizes to calculate the content bounds of the box. + /// Arranges the screen layouts inside their respective device cells and + /// scales them to fit inside their parent device layouts. /// - /// The outer bounds of the box. - /// The style of the box, which includes the sizes of the margin, border, and padding areas. - /// A object that represents the bounds of the different areas of the box. - /// Thrown when or is null. - /// Thrown when any of the styles in is null. - public static BoxBounds GetBoxBoundsFromOuterBounds( - RectangleInfo outerBounds, - BoxStyle boxStyle) + internal static void ArrangeAndScaleScreenLayouts(FormViewModel.Builder formLayout) { - ArgumentNullException.ThrowIfNull(outerBounds); - ArgumentNullException.ThrowIfNull(boxStyle); - if (outerBounds == null || boxStyle.MarginStyle == null || boxStyle.BorderStyle == null || boxStyle.PaddingStyle == null) - { - throw new ArgumentException(null, nameof(boxStyle)); - } + var deviceLayouts = formLayout.CanvasLayout?.DeviceLayouts + ?? throw new InvalidOperationException(); - var marginBounds = outerBounds; - var borderBounds = marginBounds.Shrink(boxStyle.MarginStyle); - var paddingBounds = borderBounds.Shrink(boxStyle.BorderStyle); - var contentBounds = paddingBounds.Shrink(boxStyle.PaddingStyle); - return new( - outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds); + foreach (var deviceLayout in deviceLayouts) + { + var deviceInfo = deviceLayout.DeviceInfo ?? throw new InvalidOperationException(); + var screenLayouts = deviceLayout.ScreenLayouts ?? throw new InvalidOperationException(); + if (screenLayouts.Count == 0) + { + // nothing to arrange or scale, and we'll get a divide by zero error + // in ScaleToFitRatio if we don't quit early + continue; + } + + // work out the scaling factor that will fit the screen layouts into the content bounds + var fullSizeDisplayArea = deviceInfo.GetCombinedDisplayArea(); + var contentBounds = deviceLayout.DeviceBounds.ContentBounds; + var scalingFactor = fullSizeDisplayArea.Size.ScaleToFitRatio(contentBounds.Size); + + foreach (var screenLayout in screenLayouts) + { + var screenInfo = screenLayout.ScreenInfo ?? throw new InvalidOperationException(); + screenLayout.ScreenBounds = BoxBounds.CreateFromOuterBounds( + outerBounds: screenInfo.DisplayArea + .Offset(-fullSizeDisplayArea.Left, -fullSizeDisplayArea.Top) + .Scale(scalingFactor) + .Offset(contentBounds.Left, contentBounds.Top) + .Round() + .Intersect(contentBounds), + boxStyle: screenLayout.ScreenStyle); + } + + // verify that we're pixel-perfect on the positions + var scaledScreenBounds = RectangleInfo.Union( + screenLayouts + .Select(screenLayout => screenLayout.ScreenBounds.OuterBounds)); + Debug.Assert( + (scaledScreenBounds.Width == contentBounds.Width) || (scaledScreenBounds.Height == contentBounds.Height), + string.Join( + "\r\n", + $"{nameof(ArrangeAndScaleScreenLayouts)} - scaled screen layouts do not perfectly fill content bounds:", + $"scaled grid = '{scaledScreenBounds}'", + $"content bounds = '{contentBounds}'")); + } + } + + internal static void ArrangeAndResizeCanvasLayout(FormViewModel.Builder formLayout) + { + var canvasLayout = formLayout.CanvasLayout ?? throw new InvalidOperationException(); + + // work out how big the canvas needs to be in order to contain the device layouts + var canvasContentBounds = RectangleInfo.Union( + (canvasLayout.DeviceLayouts ?? throw new InvalidOperationException()) + .Select(deviceLayout => deviceLayout.DeviceBounds.OuterBounds)); + var canvasOuterBounds = BoxBounds.CreateFromContentBounds( + contentBounds: canvasContentBounds, + boxStyle: formLayout.CanvasLayout.CanvasStyle); + + // move the canvas into position + var positionedOuterBounds = canvasOuterBounds.OuterBounds.MoveTo( + (canvasLayout.CanvasBounds ?? throw new InvalidOperationException()) + .OuterBounds.Location); + + canvasLayout.CanvasBounds = BoxBounds.CreateFromOuterBounds( + outerBounds: positionedOuterBounds, + boxStyle: formLayout.CanvasLayout.CanvasStyle); + } + + internal static void ArrangeAndResizeFormLayout(FormViewModel.Builder formLayout, ScreenInfo activatedScreen, PointInfo activatedLocation) + { + var canvasOuterBounds = formLayout.CanvasLayout?.CanvasBounds?.OuterBounds + ?? throw new InvalidOperationException(); + + // resize and center the form on the activated location + formLayout.FormBounds = canvasOuterBounds + .Center(activatedLocation) + .MoveInside(activatedScreen.DisplayArea); } } diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs index 364618fb73..91e7449b02 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs @@ -5,10 +5,12 @@ using System.ComponentModel; using System.Runtime.InteropServices; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.NativeMethods; -using static MouseJump.Common.NativeMethods.Core; -using static MouseJump.Common.NativeMethods.User32; +using MouseJump.Common.Interop; +using MouseJump.Models.Drawing; + +using Windows.Win32; +using Windows.Win32.UI.Input.KeyboardAndMouse; +using Windows.Win32.UI.WindowsAndMessaging; namespace MouseJump.Common.Helpers; @@ -35,19 +37,9 @@ public static class MouseHelper /// public static PointInfo GetCursorPosition() { - var lpPoint = new LPPOINT(new POINT(0, 0)); - var result = User32.GetCursorPos(lpPoint); - if (!result) - { - throw new Win32Exception( - Marshal.GetLastWin32Error()); - } - - var point = lpPoint.ToStructure(); - lpPoint.Free(); - - return new PointInfo( - point.x, point.y); + var result = PInvoke.GetCursorPos(out var point); + ResultHandler.ThrowIfZero(result: result, getLastError: true, memberName: nameof(PInvoke.GetCursorPos)); + return new(point.X, point.Y); } /// @@ -56,43 +48,52 @@ public static class MouseHelper /// /// See https://github.com/mikeclayton/FancyMouse/pull/3 /// - public static void SetCursorPosition(PointInfo location) + public static void SetCursorPosition(PointInfo position) + { + MouseHelper.SetCursorPositionInternal(position); + + // temporary workaround for issue #1273 + MouseHelper.SimulateMouseMovementEvent(position); + } + + private static void SetCursorPositionInternal(PointInfo position) { // set the new cursor position *twice* - the cursor sometimes end up in // the wrong place if we try to cross the dead space between non-aligned - // monitors - e.g. when trying to move the cursor from (a) to (b) we can - // *sometimes* - for no clear reason - end up at (c) instead. + // monitors - e.g. when trying to move the cursor from (a) to (b) through + // the dotted area we can *sometimes* - for no clear reason - end up at + // (c) instead. // - // +----------------+ - // |(c) (b) | - // | | - // | | - // | | + // ..........+----------------+ + // ..........|(c) (b) | + // ..........| | + // ..........| | + // ..........| | // +---------+ | // | (a) | | // +---------+----------------+ // - // setting the position again seems to fix this and moves the + // setting the position more than once seems to fix this and moves the // cursor to the expected location (b) - var target = location.ToPoint(); + var targetPosition = position.ToPoint(); for (var i = 0; i < 2; i++) { - var result = User32.SetCursorPos(target.X, target.Y); - if (!result) + // SetCursorPos has been known to return zero (i.e. an error), + // with GetLastError also returning zero to indicate success + var result1 = PInvoke.SetCursorPos(targetPosition.X, targetPosition.Y); + if (result1 == 0) { - throw new Win32Exception( - Marshal.GetLastWin32Error()); + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult(result: result1, success: lastError == 0, lastError: lastError, memberName: nameof(PInvoke.SetCursorPos)); } - var current = MouseHelper.GetCursorPosition(); - if ((current.X == target.X) || (current.Y == target.Y)) + var result2 = PInvoke.GetCursorPos(out var currentPosition); + ResultHandler.ThrowIfZero(result: result2, getLastError: true, memberName: nameof(PInvoke.GetCursorPos)); + if ((currentPosition.X == position.X) && (currentPosition.Y == position.Y)) { break; } } - - // temporary workaround for issue #1273 - MouseHelper.SimulateMouseMovementEvent(location); } /// @@ -104,27 +105,31 @@ public static class MouseHelper /// private static void SimulateMouseMovementEvent(PointInfo location) { - var inputs = new User32.INPUT[] + var inputs = new INPUT[] { - new( - type: INPUT_TYPE.INPUT_MOUSE, - data: new INPUT.DUMMYUNIONNAME( - mi: new MOUSEINPUT( - dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X), - dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y), - mouseData: 0, - dwFlags: MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, - time: 0, - dwExtraInfo: ULONG_PTR.Null))), + new() + { + type = INPUT_TYPE.INPUT_MOUSE, + Anonymous = new() + { + mi = new MOUSEINPUT + { + dx = (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X), + dy = (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y), + mouseData = 0, + dwFlags = MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, + time = 0, + dwExtraInfo = default, + }, + }, + }, }; - var result = User32.SendInput( - (UINT)inputs.Length, - new LPINPUT(inputs), - INPUT.Size * inputs.Length); + + var cbSize = Marshal.SizeOf(); + var result = PInvoke.SendInput(inputs, cbSize); if (result != inputs.Length) { - throw new Win32Exception( - Marshal.GetLastWin32Error()); + ResultHandler.HandleFailure(result: result, getLastError: true, memberName: nameof(PInvoke.SendInput)); } } @@ -132,13 +137,17 @@ public static class MouseHelper { // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput - return (x * 65535) / User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN); + var result = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN); + ResultHandler.ThrowIfZero(result: result, getLastError: false, memberName: nameof(PInvoke.GetSystemMetrics)); + return (x * 65535) / result; } private static decimal CalculateAbsoluteCoordinateY(decimal y) { // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput - return (y * 65535) / User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN); + var result = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN); + ResultHandler.ThrowIfZero(result: result, getLastError: false, memberName: nameof(PInvoke.GetSystemMetrics)); + return (y * 65535) / result; } } diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs index a863b45bbe..3ae3bb0b66 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs @@ -2,12 +2,16 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.ComponentModel; +using System.Runtime.InteropServices; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.NativeMethods; -using static MouseJump.Common.NativeMethods.Core; -using static MouseJump.Common.NativeMethods.User32; +using MouseJump.Common.Interop; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.UI.WindowsAndMessaging; namespace MouseJump.Common.Helpers; @@ -20,49 +24,47 @@ public static class ScreenHelper private static RectangleInfo GetVirtualScreen() { return new( - User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN), - User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN), - User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN), - User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN)); + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN), + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN), + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN), + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN)); } public static IEnumerable GetAllScreens() { // enumerate the monitors attached to the system var hMonitors = new List(); - var callback = new User32.MONITORENUMPROC( - (hMonitor, hdcMonitor, lprcMonitor, dwData) => - { - hMonitors.Add(hMonitor); - return true; - }); - var result = User32.EnumDisplayMonitors(HDC.Null, LPCRECT.Null, callback, LPARAM.Null); - if (!result) + unsafe { - throw new Win32Exception( - result.Value, - $"{nameof(User32.EnumDisplayMonitors)} failed with return code {result.Value}"); + var callback = new MONITORENUMPROC( + (hMonitor, hdcMonitor, lprcMonitor, dwData) => + { + hMonitors.Add(hMonitor); + return true; + }); + var result = PInvoke.EnumDisplayMonitors(HDC.Null, null, callback, (LPARAM)0); + if (result == 0) + { + throw new InvalidOperationException("failed to enumerate monitors"); + } + + // prevent callback from being collected during the enumeration + GC.KeepAlive(callback); } // get detailed info about each monitor + var monitorInfo = new MONITORINFO + { + cbSize = (uint)Marshal.SizeOf(), + }; foreach (var hMonitor in hMonitors) { - var monitorInfoPtr = new LPMONITORINFO( - new MONITORINFO((DWORD)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0)); - result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr); - if (!result) - { - throw new Win32Exception( - result.Value, - $"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}"); - } - - var monitorInfo = monitorInfoPtr.ToStructure(); - monitorInfoPtr.Free(); + var result = PInvoke.GetMonitorInfo(hMonitor, ref monitorInfo); + ResultHandler.ThrowIfZero(result: result, getLastError: true, memberName: nameof(PInvoke.GetMonitorInfo)); yield return new ScreenInfo( handle: hMonitor, - primary: monitorInfo.dwFlags.HasFlag(User32.MONITOR_INFO_FLAGS.MONITORINFOF_PRIMARY), + primary: (monitorInfo.dwFlags & PInvoke.MONITORINFOF_PRIMARY) != 0, displayArea: new RectangleInfo( monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, @@ -81,9 +83,9 @@ public static class ScreenHelper PointInfo pt) { // get the monitor handle from the point - var hMonitor = User32.MonitorFromPoint( + var hMonitor = PInvoke.MonitorFromPoint( new((int)pt.X, (int)pt.Y), - User32.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); if (hMonitor.IsNull) { throw new InvalidOperationException($"no monitor found for point {pt}"); diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs index ae2f97264d..5350d06fa6 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs @@ -2,8 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Styles; +using System.Drawing; + +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; namespace MouseJump.Common.Helpers; @@ -46,7 +48,13 @@ public static class StyleHelper color1: Color.MidnightBlue, color2: Color.MidnightBlue ) - ) + ), + extraColors: new[] + { + Color.Red, + Color.Blue, + Color.Green, + } ); /// @@ -86,7 +94,13 @@ public static class StyleHelper color1: Color.MidnightBlue, color2: Color.MidnightBlue ) - ) + ), + extraColors: new[] + { + Color.Red, + Color.Blue, + Color.Green, + } ); public static PreviewStyle WithCanvasSize(this PreviewStyle previewStyle, SizeInfo canvasSize) @@ -96,6 +110,7 @@ public static class StyleHelper return new PreviewStyle( canvasSize: canvasSize, canvasStyle: previewStyle.CanvasStyle, - screenStyle: previewStyle.ScreenStyle); + screenStyle: previewStyle.ScreenStyle, + extraColors: previewStyle.ExtraColors); } } diff --git a/src/modules/MouseUtils/MouseJump.Common/Helpers/Win32Helper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/Win32Helper.cs new file mode 100644 index 0000000000..3222341d09 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/Win32Helper.cs @@ -0,0 +1,111 @@ +// 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 System.Diagnostics; +using System.Runtime.InteropServices; + +using MouseJump.Common.Interop; + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace MouseJump.Common.Helpers; + +public static class Win32Helper +{ + /// + /// Creates a new "message only" window. + /// + /// + /// A message-only window is a window that is not visible and does not have a user interface. + /// It exists solely to receive and process messages. + /// These windows are used for background tasks that need to handle messages without displaying any graphical elements to the user. + /// + public static Win32Window CreateMessageOnlyWindow(string className, string windowName, Win32WindowProc.WindowProcDelegate windowProcDelegate) + { + ArgumentNullException.ThrowIfNullOrEmpty(className); + ArgumentNullException.ThrowIfNullOrEmpty(windowName); + ArgumentNullException.ThrowIfNull(windowProcDelegate); + + var windowClass = Win32Helper.RegisterWindowClass(className, windowProcDelegate); + var window = Win32Helper.CreateWindow(windowClass, windowName); + + return window; + } + + private static Win32WindowClass RegisterWindowClass(string className, Win32WindowProc.WindowProcDelegate windowProcDelegate) + { + ArgumentNullException.ThrowIfNullOrEmpty(className); + ArgumentNullException.ThrowIfNull(windowProcDelegate); + + using var hModule = PInvoke.GetModuleHandle(null); + var hInstance = (HINSTANCE)hModule.DangerousGetHandle(); + + // register the window class + // see https://stackoverflow.com/a/30992796/3156906 + var windowProc = new Win32WindowProc(windowProcDelegate); + var classAtom = default(ushort); + unsafe + { + fixed (char* classNamePtr = className) + { + var wndClass = new WNDCLASSEXW + { + cbSize = (uint)Marshal.SizeOf(), + style = 0, + lpfnWndProc = windowProc.WndProcDelegate, + cbClsExtra = 0, + cbWndExtra = 0, + hInstance = hInstance, + hIcon = HICON.Null, + hCursor = HCURSOR.Null, + hbrBackground = HBRUSH.Null, + lpszMenuName = default, + lpszClassName = new PCWSTR(classNamePtr), + hIconSm = HICON.Null, + }; + classAtom = PInvoke.RegisterClassEx(wndClass); + } + } + + ResultHandler.ThrowIfZero(result: classAtom, getLastError: true, memberName: nameof(PInvoke.RegisterClassEx)); + + var windowClass = new Win32WindowClass(classAtom, className, windowProc); + return windowClass; + } + + private static Win32Window CreateWindow(Win32WindowClass windowClass, string windowName) + { + // create the window + // see https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows + // https://devblogs.microsoft.com/oldnewthing/20171218-00/?p=97595 + // https://stackoverflow.com/a/30992796/3156906 + var hWnd = default(HWND); + + using var hModule = PInvoke.GetModuleHandle(null); + + unsafe + { + hWnd = PInvoke.CreateWindowEx( + dwExStyle: 0, + lpClassName: windowClass.ClassName, + lpWindowName: windowName, + dwStyle: 0, + X: 0, + Y: 0, + nWidth: 300, + nHeight: 400, + hWndParent: HWND.HWND_MESSAGE, // message-only window + hMenu: null, + hInstance: hModule, + lpParam: null); + } + + ResultHandler.ThrowIfZero(result: hWnd, getLastError: true, memberName: nameof(PInvoke.CreateWindowEx)); + + var window = new Win32Window(windowClass, windowName, hWnd); + return window; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs index 07ba85d09c..de339cf4e6 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs @@ -3,10 +3,14 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Drawing; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.NativeMethods; -using static MouseJump.Common.NativeMethods.Core; +using MouseJump.Common.Interop; +using MouseJump.Models.Drawing; + +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; namespace MouseJump.Common.Imaging; @@ -28,12 +32,12 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService var stopwatch = Stopwatch.StartNew(); var (desktopHwnd, desktopHdc) = DesktopImageRegionCopyService.GetDesktopDeviceContext(); var previewHdc = DesktopImageRegionCopyService.GetGraphicsDeviceContext( - targetGraphics, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE); + targetGraphics, STRETCH_BLT_MODE.STRETCH_HALFTONE); stopwatch.Stop(); var source = sourceBounds.ToRectangle(); var target = targetBounds.ToRectangle(); - var result = Gdi32.StretchBlt( + var result = PInvoke.StretchBlt( previewHdc, target.X, target.Y, @@ -44,15 +48,11 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService source.Y, source.Width, source.Height, - Gdi32.ROP_CODE.SRCCOPY); - if (!result) - { - throw new InvalidOperationException( - $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); - } + ROP_CODE.SRCCOPY); + ResultHandler.ThrowIfZero(result: result, getLastError: false, memberName: nameof(PInvoke.StretchBlt)); // we need to release the graphics device context handle before anything - // else tries to use the Graphics object otherwise it'll give an error + // else tries to use the Graphics object - otherwise it'll give an error // from GDI saying "Object is currently in use elsewhere" DesktopImageRegionCopyService.FreeGraphicsDeviceContext(targetGraphics, ref previewHdc); @@ -61,14 +61,9 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService private static (HWND DesktopHwnd, HDC DesktopHdc) GetDesktopDeviceContext() { - var desktopHwnd = User32.GetDesktopWindow(); - var desktopHdc = User32.GetWindowDC(desktopHwnd); - if (desktopHdc.IsNull) - { - throw new InvalidOperationException( - $"{nameof(User32.GetWindowDC)} returned null"); - } - + var desktopHwnd = PInvoke.GetDesktopWindow(); + var desktopHdc = PInvoke.GetWindowDC(desktopHwnd); + ResultHandler.HandleResult(result: desktopHdc, success: !desktopHdc.IsNull, getLastError: false, memberName: nameof(PInvoke.GetWindowDC)); return (desktopHwnd, desktopHdc); } @@ -76,12 +71,8 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService { if (!desktopHwnd.IsNull && !desktopHdc.IsNull) { - var result = User32.ReleaseDC(desktopHwnd, desktopHdc); - if (result == 0) - { - throw new InvalidOperationException( - $"{nameof(User32.ReleaseDC)} returned {result}"); - } + var result = PInvoke.ReleaseDC(desktopHwnd, desktopHdc); + ResultHandler.ThrowIfZero(result: result, getLastError: false, memberName: nameof(PInvoke.ReleaseDC)); } desktopHwnd = HWND.Null; @@ -92,17 +83,11 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService /// Checks if the target device context handle exists, and creates a new one from the /// specified Graphics object if not. /// - private static HDC GetGraphicsDeviceContext(Graphics graphics, Gdi32.STRETCH_BLT_MODE mode) + private static HDC GetGraphicsDeviceContext(Graphics graphics, STRETCH_BLT_MODE mode) { var graphicsHdc = (HDC)graphics.GetHdc(); - - var result = Gdi32.SetStretchBltMode(graphicsHdc, mode); - if (result == 0) - { - throw new InvalidOperationException( - $"{nameof(Gdi32.SetStretchBltMode)} returned {result}"); - } - + var result = PInvoke.SetStretchBltMode(graphicsHdc, mode); + ResultHandler.ThrowIfZero(result: result, getLastError: false, memberName: nameof(PInvoke.SetStretchBltMode)); return graphicsHdc; } @@ -116,7 +101,7 @@ public sealed class DesktopImageRegionCopyService : IImageRegionCopyService return; } - graphics.ReleaseHdc(graphicsHdc.Value); + graphics.ReleaseHdc(graphicsHdc); graphicsHdc = HDC.Null; } } diff --git a/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs index 54f0681328..b397e54570 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MouseJump.Common.Models.Drawing; +using System.Drawing; + +using MouseJump.Models.Drawing; namespace MouseJump.Common.Imaging; @@ -10,7 +12,7 @@ public interface IImageRegionCopyService { /// /// Copies the source region from the provider's source image (e.g. the interactive desktop, - /// a static image, etc.) to the target region on the specified Graphics object. + /// a static image, etc) to the target region on the specified Graphics object. /// /// /// Implementations of this interface are used to capture regions of the interactive desktop diff --git a/src/modules/MouseUtils/MouseJump.Common/Imaging/SolidColorRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/SolidColorRegionCopyService.cs new file mode 100644 index 0000000000..bf43a134ec --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/SolidColorRegionCopyService.cs @@ -0,0 +1,47 @@ +// 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 System.Drawing; +using System.Drawing.Drawing2D; + +using MouseJump.Models.Drawing; + +namespace MouseJump.Common.Imaging; + +/// +/// Implements an IImageRegionCopyService that paints a solid block of the specified color. +/// This is used for testing the DrawingHelper rather than as part of the main application. +/// +public sealed class SolidColorRegionCopyService : IImageRegionCopyService +{ + public SolidColorRegionCopyService(Color color) + { + this.Color = color; + } + + private Color Color + { + get; + } + + /// + /// Draws a region in a solid color. + /// + public void CopyImageRegion( + Graphics targetGraphics, + RectangleInfo sourceBounds, + RectangleInfo targetBounds) + { + using var brush = new SolidBrush(this.Color); + + // prevent the background bleeding through into screen images + // (see https://github.com/mikeclayton/FancyMouse/issues/44) + targetGraphics.PixelOffsetMode = PixelOffsetMode.Half; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + + targetGraphics.FillRectangle( + brush: brush, + rect: targetBounds.ToRectangle()); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs index 5f0304e4cc..2950f6967b 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs @@ -2,9 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Drawing; using System.Drawing.Drawing2D; -using MouseJump.Common.Models.Drawing; +using MouseJump.Models.Drawing; namespace MouseJump.Common.Imaging; diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_int.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_int.cs new file mode 100644 index 0000000000..660a43faf3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_int.cs @@ -0,0 +1,121 @@ +// 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 System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MouseJump.Common.Interop; + +public static partial class ResultHandler +{ + /* + helpers for handling results from Win32 API calls that return an int value, + throwing exceptions when errors occur, and reading the last error code when necessary. + */ + + public static void ThrowIfZero( + int result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + ResultHandler.HandleResult( + result, + result != 0, + getLastError, + memberName); + } + + public static void ThrowIfNotZero( + int result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + ResultHandler.HandleResult( + result, + result == 0, + getLastError, + memberName); + } + + public static void HandleResult( + int result, + bool success, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + ResultHandler.HandleFailure(result, getLastError, memberName); + } + + public static void HandleResult( + int result, + bool success, + Func? getLastError, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + var lastError = getLastError?.Invoke(); + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleResult( + int result, + bool success, + int? lastError, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + int result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError ? (int?)Marshal.GetLastPInvokeError() : null; + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + int result, + Func? getLastError, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError?.Invoke(); + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + int result, + int? lastError, + [CallerMemberName] string memberName = "") + { + var lines = new List + { + $"{memberName} failed with result {result}.", + }; + + if (lastError is not null) + { + lines.Add($"last error was '{lastError}'"); + } + + var message = string.Join(Environment.NewLine, lines); + throw new Win32Exception(message); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_nint.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_nint.cs new file mode 100644 index 0000000000..2bc1a53b0c --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_nint.cs @@ -0,0 +1,121 @@ +// 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 System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MouseJump.Common.Interop; + +public static partial class ResultHandler +{ + /* + helpers for handling results from Win32 API calls that return an nint value, + throwing exceptions when errors occur, and reading the last error code when necessary. + */ + + public static void ThrowIfZero( + nint result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + ResultHandler.HandleResult( + result, + result.ToInt64() != 0, + getLastError, + memberName); + } + + public static void ThrowIfNotZero( + nint result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + ResultHandler.HandleResult( + result, + result.ToInt64() == 0, + getLastError, + memberName); + } + + public static void HandleResult( + nint result, + bool success, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + ResultHandler.HandleFailure(result, getLastError, memberName); + } + + public static void HandleResult( + nint result, + bool success, + Func? getLastError, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + var lastError = getLastError?.Invoke(); + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleResult( + nint result, + bool success, + int? lastError, + [CallerMemberName] string memberName = "") + { + if (success) + { + return; + } + + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + nint result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError ? (int?)Marshal.GetLastPInvokeError() : null; + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + nint result, + Func? getLastError, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError?.Invoke(); + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + nint result, + int? lastError, + [CallerMemberName] string memberName = "") + { + var lines = new List + { + $"{memberName} failed with result {result}.", + }; + + if (lastError is not null) + { + lines.Add($"last error was '{lastError}'"); + } + + var message = string.Join(Environment.NewLine, lines); + throw new Win32Exception(message); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_uint.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_uint.cs new file mode 100644 index 0000000000..1457818a36 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/ResultHandler_uint.cs @@ -0,0 +1,54 @@ +// 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 System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MouseJump.Common.Interop; + +public static partial class ResultHandler +{ + /* + helpers for handling results from Win32 API calls that return a uint value, + throwing exceptions when errors occur, and reading the last error code when necessary. + */ + + public static void HandleFailure( + uint result, + bool getLastError = false, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError ? (int?)Marshal.GetLastPInvokeError() : null; + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + uint result, + Func? getLastError, + [CallerMemberName] string memberName = "") + { + var lastError = getLastError?.Invoke(); + ResultHandler.HandleFailure(result, lastError, memberName); + } + + public static void HandleFailure( + uint result, + int? lastError, + [CallerMemberName] string memberName = "") + { + var lines = new List + { + $"{memberName} failed with result {result}.", + }; + + if (lastError is not null) + { + lines.Add($"last error was '{lastError}'"); + } + + var message = string.Join(Environment.NewLine, lines); + throw new Win32Exception(message); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/Win32SafeHandle.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32SafeHandle.cs new file mode 100644 index 0000000000..9fec8c20fd --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32SafeHandle.cs @@ -0,0 +1,42 @@ +// 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 System.Runtime.InteropServices; + +namespace MouseJump.Common.Interop; + +public sealed class Win32SafeHandle : SafeHandle +{ + public Win32SafeHandle(nint handle) + : base(IntPtr.Zero, false) + { + this.SetHandle(handle); + } + + public Win32SafeHandle(nint handle, bool ownsHandle = false) + : base(IntPtr.Zero, ownsHandle) + { + this.SetHandle(handle); + } + + public Win32SafeHandle(nint handle, bool ownsHandle, Func release) + : base(IntPtr.Zero, ownsHandle) + { + this.ReleaseDelegate = release ?? throw new ArgumentNullException(nameof(release)); + this.SetHandle(handle); + } + + private Func? ReleaseDelegate + { + get; + } + + public override bool IsInvalid + => handle == nint.Zero; + + protected override bool ReleaseHandle() + { + return this.ReleaseDelegate?.Invoke(this.handle) ?? true; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/Win32Window.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32Window.cs new file mode 100644 index 0000000000..054cca1f9c --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32Window.cs @@ -0,0 +1,32 @@ +// 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 Windows.Win32.Foundation; + +namespace MouseJump.Common.Interop; + +public sealed class Win32Window +{ + internal Win32Window(Win32WindowClass windowClass, string windowName, HWND hwnd) + { + this.WindowClass = windowClass ?? throw new ArgumentNullException(nameof(windowClass)); + this.WindowName = windowName ?? throw new ArgumentNullException(nameof(windowName)); + this.Hwnd = hwnd; + } + + public Win32WindowClass WindowClass + { + get; + } + + public string WindowName + { + get; + } + + public nint Hwnd + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowClass.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowClass.cs new file mode 100644 index 0000000000..a9425116f0 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowClass.cs @@ -0,0 +1,38 @@ +// 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 Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace MouseJump.Common.Interop; + +/// +/// Wrapper around a Windows window class that can be passed to +/// external assemblies to manage WNDPROC delegate lifetime without +/// exposing internals. +/// +public sealed class Win32WindowClass +{ + internal Win32WindowClass(ushort classAtom, string className, Win32WindowProc windowProc) + { + this.ClassAtom = classAtom; + this.ClassName = className ?? throw new ArgumentNullException(nameof(className)); + this.WindowProc = windowProc ?? throw new ArgumentNullException(nameof(windowProc)); + } + + internal ushort ClassAtom + { + get; + } + + public string ClassName + { + get; + } + + internal Win32WindowProc WindowProc + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowProc.cs b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowProc.cs new file mode 100644 index 0000000000..4f1d90a3f4 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/Interop/Win32WindowProc.cs @@ -0,0 +1,71 @@ +// 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 Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace MouseJump.Common.Interop; + +/// +/// Wrapper around a WNDPROC delegate that can be handed to +/// external assemblies to manage native WNDPROC delegate lifetime +/// without exposing internals. +/// +public sealed class Win32WindowProc +{ + /// + /// Public definition of WNDPROC using CLR types instead of CsWin32 generated types. + /// + public delegate nint WindowProcDelegate(nint hWnd, uint msg, nuint wParam, nint lParam); + + internal Win32WindowProc(WindowProcDelegate windowProc) + { + this.WindowProcInternal = windowProc ?? throw new ArgumentNullException(nameof(windowProc)); + + // tl;dr... + // + // * Native code stores only the unmanaged function pointer. + // * Managed code must keep the delegate alive for native code to call. + // + // long version... + // + // A *method group* (e.g. WndProcInternal) is *not* a delegate (WNDPROC). + // When a method group is passed to anything that receives a delegate, the + // CLR creates a new temporary delegate instance based on the method group. + // + // Windows api methods such as RegisterClassEx don't store a reference to + // the managed delegate object - they only store an unmanaged function + // pointer. Once the delegate instance becomes unreferenced on the managed + // side the GC is allowed to collect it, even though the Win32 api continues + // to store the unmanaged pointer to it. If Win32 subsequently calls the + // (now invalid) unmanaged function pointer it can cause access violations + // or other runtime crashes. + // + // To prevent this, we'll cache a strongly-typed reference to the managed + // *delegate* instance in WndProcDelegate. Keeping this delegate reference + // alive ensures the underlying unmanaged function pointer remains valid + // for the entire lifetime of this Window instance. + this.WndProcDelegate = this.WndProcInternal; + } + + internal ushort ClassAtom + { + get; + } + + private WindowProcDelegate WindowProcInternal + { + get; + } + + internal WNDPROC WndProcDelegate + { + get; + } + + private LRESULT WndProcInternal(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) + { + return (LRESULT)this.WindowProcInternal(hWnd, msg, wParam, lParam); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs deleted file mode 100644 index 0c2d81a196..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs +++ /dev/null @@ -1,73 +0,0 @@ -// 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. - -namespace MouseJump.Common.Models.Drawing; - -public sealed class BoxBounds -{ - /* - - see https://www.w3schools.com/css/css_boxmodel.asp - - +--------------[bounds]---------------+ - |▒▒▒▒▒▒▒▒▒▒▒▒▒▒[margin]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| - |▒▒▓▓▓▓▓▓▓▓▓▓▓▓[border]▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒| - |▒▒▓▓░░░░░░░░░░[padding]░░░░░░░░░░▓▓▒▒| - |▒▒▓▓░░ ░░▓▓▒▒| - |▒▒▓▓░░ ░░▓▓▒▒| - |▒▒▓▓░░ [content] ░░▓▓▒▒| - |▒▒▓▓░░ ░░▓▓▒▒| - |▒▒▓▓░░ ░░▓▓▒▒| - |▒▒▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▒▒| - |▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒| - |▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| - +-------------------------------------+ - - */ - - public BoxBounds( - RectangleInfo outerBounds, - RectangleInfo marginBounds, - RectangleInfo borderBounds, - RectangleInfo paddingBounds, - RectangleInfo contentBounds) - { - this.OuterBounds = outerBounds ?? throw new ArgumentNullException(nameof(outerBounds)); - this.MarginBounds = marginBounds ?? throw new ArgumentNullException(nameof(marginBounds)); - this.BorderBounds = borderBounds ?? throw new ArgumentNullException(nameof(borderBounds)); - this.PaddingBounds = paddingBounds ?? throw new ArgumentNullException(nameof(paddingBounds)); - this.ContentBounds = contentBounds ?? throw new ArgumentNullException(nameof(contentBounds)); - } - - /// - /// Gets the outer bounds of this layout box. - /// - public RectangleInfo OuterBounds - { - get; - } - - public RectangleInfo MarginBounds - { - get; - } - - public RectangleInfo BorderBounds - { - get; - } - - public RectangleInfo PaddingBounds - { - get; - } - - /// - /// Gets the bounds of the content area for this layout box. - /// - public RectangleInfo ContentBounds - { - get; - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs deleted file mode 100644 index 342389d45d..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs +++ /dev/null @@ -1,131 +0,0 @@ -// 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 System.Collections.ObjectModel; - -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Styles; - -namespace MouseJump.Common.Models.Layout; - -public sealed class PreviewLayout -{ - public sealed class Builder - { - public Builder() - { - this.Screens = new(); - this.ScreenshotBounds = new(); - } - - public PreviewStyle? PreviewStyle - { - get; - set; - } - - public RectangleInfo? VirtualScreen - { - get; - set; - } - - public List Screens - { - get; - set; - } - - public int ActivatedScreenIndex - { - get; - set; - } - - public RectangleInfo? FormBounds - { - get; - set; - } - - public BoxBounds? PreviewBounds - { - get; - set; - } - - public List ScreenshotBounds - { - get; - set; - } - - public PreviewLayout Build() - { - return new PreviewLayout( - previewStyle: this.PreviewStyle ?? throw new InvalidOperationException($"{nameof(this.PreviewStyle)} must be initialized before calling {nameof(this.Build)}."), - virtualScreen: this.VirtualScreen ?? throw new InvalidOperationException($"{nameof(this.VirtualScreen)} must be initialized before calling {nameof(this.Build)}."), - screens: this.Screens ?? throw new InvalidOperationException($"{nameof(this.Screens)} must be initialized before calling {nameof(this.Build)}."), - activatedScreenIndex: this.ActivatedScreenIndex, - formBounds: this.FormBounds ?? throw new InvalidOperationException($"{nameof(this.FormBounds)} must be initialized before calling {nameof(this.Build)}."), - previewBounds: this.PreviewBounds ?? throw new InvalidOperationException($"{nameof(this.PreviewBounds)} must be initialized before calling {nameof(this.Build)}."), - screenshotBounds: this.ScreenshotBounds ?? throw new InvalidOperationException($"{nameof(this.ScreenshotBounds)} must be initialized before calling {nameof(this.Build)}.")); - } - } - - public PreviewLayout( - PreviewStyle previewStyle, - RectangleInfo virtualScreen, - List screens, - int activatedScreenIndex, - RectangleInfo formBounds, - BoxBounds previewBounds, - List screenshotBounds) - { - this.PreviewStyle = previewStyle ?? throw new ArgumentNullException(nameof(previewStyle)); - this.VirtualScreen = virtualScreen ?? throw new ArgumentNullException(nameof(virtualScreen)); - this.Screens = (screens ?? throw new ArgumentNullException(nameof(screens))) - .ToList().AsReadOnly(); - this.ActivatedScreenIndex = activatedScreenIndex; - this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); - this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds)); - this.ScreenshotBounds = (screenshotBounds ?? throw new ArgumentNullException(nameof(screenshotBounds))) - .ToList().AsReadOnly(); - } - - public PreviewStyle PreviewStyle - { - get; - } - - public RectangleInfo VirtualScreen - { - get; - } - - public ReadOnlyCollection Screens - { - get; - } - - public int ActivatedScreenIndex - { - get; - } - - public RectangleInfo FormBounds - { - get; - } - - public BoxBounds PreviewBounds - { - get; - } - - public ReadOnlyCollection ScreenshotBounds - { - get; - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj b/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj index 79d6b7b47e..f94b2971d0 100644 --- a/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj +++ b/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj @@ -1,24 +1,32 @@ - - - + + + - + + {923DF87C-CA99-4D1C-B1D2-959174E95BFA} + PowerToys.MouseJump.Common + PowerToys.MouseJump.Common + PowerToys MouseJump.Common + Library + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps + false + false - {923DF87C-CA99-4D1C-B1D2-959174E95BFA} - PowerToys.MouseJump.Common - PowerToys.MouseJump.Common - PowerToys MouseJump.Common - Library - $(RepoRoot)$(Platform)\$(Configuration) - false - false + PerMonitorV2 + true + enable + enable + - PerMonitorV2 - true - enable - true - enable - - + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods.txt b/src/modules/MouseUtils/MouseJump.Common/NativeMethods.txt new file mode 100644 index 0000000000..9274e7b27e --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods.txt @@ -0,0 +1,69 @@ +HBRUSH +HCURSOR +HICON +HINSTANCE + +WM_ACTIVATE +WM_CREATE +WM_ENTERIDLE +WM_ENTERMENULOOP +WM_EXITMENULOOP +WM_KILLFOCUS +WM_GETMINMAXINFO +WM_IME_SETCONTEXT +WM_IME_NOTIFY +WM_INITMENU +WM_INITMENUPOPUP +WM_LBUTTONDOWN +WM_MENUSELECT +WM_NCACTIVATE +WM_NCCALCSIZE +WM_NCCREATE +WM_RBUTTONDOWN +WM_SETCURSOR +WM_SETFOCUS +WM_UNINITMENUPOPUP +WM_USER +WM_WINDOWPOSCHANGED +WM_WINDOWPOSCHANGING +WM_COMMAND + +CreateWindowExW +DefWindowProc +GetWindowLong +RegisterClassEx +SetForegroundWindow +WNDPROC + +MONITORINFOF_PRIMARY +ScreenToClient +EnumDisplayMonitors +GetMonitorInfo +GetSystemMetrics +MonitorFromPoint +SendInput + +GetCursorPos +SetCursorPos + +AppendMenu +CreatePopupMenu +SetMenuInfo +TrackPopupMenuEx +TRACK_POPUP_MENU_FLAGS + +SetStretchBltMode +StretchBlt + +GetDesktopWindow +GetWindowDC +ReleaseDC + +Shell_NotifyIconW + +GetAwarenessFromDpiAwarenessContext +GetThreadDpiAwarenessContext + +HWND_MESSAGE + +GetModuleHandleW \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs deleted file mode 100644 index 03e3d8efd1..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// The CRECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect - /// - [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] - internal readonly struct CRECT - { - public static readonly CRECT Empty = new(0, 0, 0, 0); - - public readonly LONG left; - public readonly LONG top; - public readonly LONG right; - public readonly LONG bottom; - - public CRECT( - int left, int top, int right, int bottom) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public CRECT( - LONG left, LONG top, LONG right, LONG bottom) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public static int Size => - Marshal.SizeOf(typeof(CRECT)); - - public override string ToString() - { - return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs deleted file mode 100644 index 038ae8df2b..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// A handle to a device context (DC). - /// This type is declared in WinDef.h as follows: - /// typedef HANDLE HDC; - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct HDC - { - public static readonly HDC Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HDC(IntPtr value) - { - this.Value = value; - } - - public bool IsNull => this.Value == HDC.Null.Value; - - public static implicit operator IntPtr(HDC value) => value.Value; - - public static explicit operator HDC(IntPtr value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs deleted file mode 100644 index 9344208770..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// A handle to a display monitor. - /// This type is declared in WinDef.h as follows: - /// if(WINVER >= 0x0500) typedef HANDLE HMONITOR; - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct HMONITOR - { - public static readonly HMONITOR Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HMONITOR(IntPtr value) - { - this.Value = value; - } - - public bool IsNull => this.Value == HMONITOR.Null.Value; - - public static implicit operator int(HMONITOR value) => value.Value.ToInt32(); - - public static explicit operator HMONITOR(int value) => new(value); - - public static implicit operator IntPtr(HMONITOR value) => value.Value; - - public static explicit operator HMONITOR(IntPtr value) => new(value); - - public static implicit operator HANDLE(HMONITOR value) => new(value.Value); - - public static explicit operator HMONITOR(HANDLE value) => new(value.Value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs deleted file mode 100644 index 3ac457eb44..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// A 32-bit signed integer.The range is -2147483648 through 2147483647 decimal. - /// This type is declared in WinNT.h as follows: - /// typedef long LONG; - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct LONG - { - public readonly int Value; - - public LONG(int value) - { - this.Value = value; - } - - public static implicit operator int(LONG value) => value.Value; - - public static implicit operator LONG(int value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs deleted file mode 100644 index 5cc6d9f3c7..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - internal readonly struct LPCRECT - { - public static readonly LPCRECT Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public LPCRECT(IntPtr value) - { - this.Value = value; - } - - public LPCRECT(CRECT value) - { - this.Value = LPCRECT.ToPtr(value); - } - - public bool IsNull => this.Value == LPCRECT.Null.Value; - - private static IntPtr ToPtr(CRECT value) - { - var ptr = Marshal.AllocHGlobal(CRECT.Size); - Marshal.StructureToPtr(value, ptr, false); - return ptr; - } - - public void Free() - { - Marshal.FreeHGlobal(this.Value); - } - - public static implicit operator IntPtr(LPCRECT value) => value.Value; - - public static explicit operator LPCRECT(IntPtr value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs deleted file mode 100644 index 725cc154d1..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - internal readonly struct LPPOINT - { - public static readonly LPPOINT Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public LPPOINT(IntPtr value) - { - this.Value = value; - } - - public LPPOINT(POINT value) - { - this.Value = LPPOINT.ToPtr(value); - } - - public bool IsNull => this.Value == LPPOINT.Null.Value; - - private static IntPtr ToPtr(POINT value) - { - var ptr = Marshal.AllocHGlobal(POINT.Size); - Marshal.StructureToPtr(value, ptr, false); - return ptr; - } - - public POINT ToStructure() - { - return Marshal.PtrToStructure(this.Value); - } - - public void Free() - { - Marshal.FreeHGlobal(this.Value); - } - - public static implicit operator IntPtr(LPPOINT value) => value.Value; - - public static explicit operator LPPOINT(IntPtr value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs deleted file mode 100644 index 31ba69fd4c..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - internal readonly struct LPRECT - { - public static readonly LPRECT Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public LPRECT(IntPtr value) - { - this.Value = value; - } - - public LPRECT(RECT value) - { - this.Value = LPRECT.ToPtr(value); - } - - public bool IsNull => this.Value == LPRECT.Null.Value; - - private static IntPtr ToPtr(RECT value) - { - var ptr = Marshal.AllocHGlobal(RECT.Size); - Marshal.StructureToPtr(value, ptr, false); - return ptr; - } - - public void Free() - { - Marshal.FreeHGlobal(this.Value); - } - - public static implicit operator IntPtr(LPRECT value) => value.Value; - - public static explicit operator LPRECT(IntPtr value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs deleted file mode 100644 index a843059b99..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// The POINT structure defines the x- and y-coordinates of a point. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")] - internal readonly struct POINT - { - /// - /// Specifies the x-coordinate of the point. - /// - public readonly LONG x; - - /// - /// Specifies the y-coordinate of the point. - /// - public readonly LONG y; - - public POINT( - int x, - int y) - { - this.x = x; - this.y = y; - } - - public POINT( - LONG x, - LONG y) - { - this.x = x; - this.y = y; - } - - public static int Size => - Marshal.SizeOf(typeof(POINT)); - - public override string ToString() - { - return $"x={this.x},y={this.y}"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs deleted file mode 100644 index 04df9a98c8..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect - /// - [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] - internal readonly struct RECT - { - public static readonly RECT Empty = new(0, 0, 0, 0); - - public readonly LONG left; - public readonly LONG top; - public readonly LONG right; - public readonly LONG bottom; - - public RECT( - int left, int top, int right, int bottom) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public RECT( - LONG left, LONG top, LONG right, LONG bottom) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public static int Size => - Marshal.SizeOf(typeof(RECT)); - - public override string ToString() - { - return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs deleted file mode 100644 index f178f8f866..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// An unsigned INT. The range is 0 through 4294967295 decimal. - /// This type is declared in WinDef.h as follows: - /// typedef unsigned int UINT; - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct UINT - { - public readonly uint Value; - - public UINT(uint value) - { - this.Value = value; - } - - public static implicit operator uint(UINT value) => value.Value; - - public static implicit operator UINT(uint value) => new(value); - - public static explicit operator int(UINT value) => (int)value.Value; - - public static explicit operator UINT(int value) => new((uint)value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs deleted file mode 100644 index b8a8764c3a..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// An unsigned LONG_PTR. - /// This type is declared in BaseTsd.h as follows: - /// C++ - /// #if defined(_WIN64) - /// typedef unsigned __int64 ULONG_PTR; - /// #else - /// typedef unsigned long ULONG_PTR; - /// #endif - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct ULONG_PTR - { - public static readonly ULONG_PTR Null = new(UIntPtr.Zero); - - public readonly UIntPtr Value; - - public ULONG_PTR(UIntPtr value) - { - this.Value = value; - } - - public static implicit operator UIntPtr(ULONG_PTR value) => value.Value; - - public static explicit operator ULONG_PTR(UIntPtr value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs deleted file mode 100644 index 8ef05dd17a..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Core -{ - /// - /// A 16-bit unsigned integer.The range is 0 through 65535 decimal. - /// This type is declared in WinDef.h as follows: - /// typedef unsigned short WORD; - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types - /// - internal readonly struct WORD - { - public readonly ushort Value; - - public WORD(ushort value) - { - this.Value = value; - } - - public static implicit operator ulong(WORD value) => value.Value; - - public static implicit operator WORD(ushort value) => new(value); - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs deleted file mode 100644 index 4f4a9673c4..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Gdi32 -{ - /// - /// A raster-operation code. These codes define how the color data for the source - /// rectangle is to be combined with the color data for the destination rectangle - /// to achieve the final color. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt - /// - internal enum ROP_CODE : uint - { - BLACKNESS = 0x00000042, - CAPTUREBLT = 0x40000000, - DSTINVERT = 0x00550009, - MERGECOPY = 0x00C000CA, - MERGEPAINT = 0x00BB0226, - NOMIRRORBITMAP = 0x80000000, - NOTSRCCOPY = 0x00330008, - NOTSRCERASE = 0x001100A6, - PATCOPY = 0x00F00021, - PATINVERT = 0x005A0049, - PATPAINT = 0x00FB0A09, - SRCAND = 0x008800C6, - SRCCOPY = 0x00CC0020, - SRCERASE = 0x00440328, - SRCINVERT = 0x00660046, - SRCPAINT = 0x00EE0086, - WHITENESS = 0x00FF0062, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs deleted file mode 100644 index 0b25a3d565..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Gdi32 -{ - /// - /// The stretching mode. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode - /// - internal enum STRETCH_BLT_MODE : int - { - BLACKONWHITE = 1, - COLORONCOLOR = 3, - HALFTONE = 4, - WHITEONBLACK = 2, - STRETCH_ANDSCANS = STRETCH_BLT_MODE.BLACKONWHITE, - STRETCH_DELETESCANS = STRETCH_BLT_MODE.COLORONCOLOR, - STRETCH_HALFTONE = STRETCH_BLT_MODE.HALFTONE, - STRETCH_ORSCANS = STRETCH_BLT_MODE.WHITEONBLACK, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs deleted file mode 100644 index 95c71137a5..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Gdi32 -{ - /// - /// The SetStretchBltMode function sets the bitmap stretching mode in the specified device context. - /// - /// - /// If the function succeeds, the return value is the previous stretching mode. - /// If the function fails, the return value is zero. - /// This function can return the following value. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode - /// - [LibraryImport(Libraries.Gdi32)] - internal static partial int SetStretchBltMode( - HDC hdc, - STRETCH_BLT_MODE mode); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs deleted file mode 100644 index f6f9243815..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class Gdi32 -{ - /// - /// The StretchBlt function copies a bitmap from a source rectangle into a destination - /// rectangle, stretching or compressing the bitmap to fit the dimensions of the - /// destination rectangle, if necessary. The system stretches or compresses the bitmap - /// according to the stretching mode currently set in the destination device context. - /// - /// - /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt - /// - [LibraryImport(Libraries.Gdi32)] - internal static partial BOOL StretchBlt( - HDC hdcDest, - int xDest, - int yDest, - int wDest, - int hDest, - HDC hdcSrc, - int xSrc, - int ySrc, - int wSrc, - int hSrc, - ROP_CODE rop); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs deleted file mode 100644 index 7ebbea2dfa..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// The EnumDisplayMonitors function enumerates display monitors (including invisible - /// pseudo-monitors associated with the mirroring drivers) that intersect a region formed - /// by the intersection of a specified clipping rectangle and the visible region of a - /// device context. EnumDisplayMonitors calls an application-defined MonitorEnumProc - /// callback function once for each monitor that is enumerated. Note that - /// GetSystemMetrics (SM_CMONITORS) counts only the display monitors. - /// - /// - /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors - /// - [LibraryImport(Libraries.User32)] - internal static partial BOOL EnumDisplayMonitors( - HDC hdc, - LPCRECT lprcClip, - MONITORENUMPROC lpfnEnum, - LPARAM dwData); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs deleted file mode 100644 index 229dedcdf1..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// The GetWindowDC function retrieves the device context (DC) for the entire window, - /// including title bar, menus, and scroll bars. A window device context permits painting - /// anywhere in a window, because the origin of the device context is the upper-left - /// corner of the window instead of the client area. - /// - /// GetWindowDC assigns default attributes to the window device context each time it - /// retrieves the device context. Previous attributes are lost. - /// - /// - /// If the function succeeds, the return value is a handle to a device context for the specified window. - /// If the function fails, the return value is NULL, indicating an error or an invalid hWnd parameter. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc - /// - [LibraryImport(Libraries.User32)] - internal static partial HDC GetWindowDC( - HWND hWnd); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs deleted file mode 100644 index d70342fa0b..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput - /// - internal readonly struct LPMONITORINFO - { - public static readonly LPMONITORINFO Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public LPMONITORINFO(IntPtr value) - { - this.Value = value; - } - - public LPMONITORINFO(MONITORINFO value) - { - this.Value = LPMONITORINFO.ToPtr(value); - } - - public MONITORINFO ToStructure() - { - return Marshal.PtrToStructure(this.Value); - } - - private static IntPtr ToPtr(MONITORINFO value) - { - var ptr = Marshal.AllocHGlobal(MONITORINFO.Size); - Marshal.StructureToPtr(value, ptr, false); - return ptr; - } - - public void Free() - { - Marshal.FreeHGlobal(this.Value); - } - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs deleted file mode 100644 index 017f8a2d72..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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 static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// A MonitorEnumProc function is an application-defined callback function that is called by the EnumDisplayMonitors function. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc - /// - internal delegate BOOL MONITORENUMPROC( - HMONITOR unnamedParam1, - HDC unnamedParam2, - LPRECT unnamedParam3, - LPARAM unnamedParam4); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs deleted file mode 100644 index 06c5a07b7e..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs +++ /dev/null @@ -1,40 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] - [StructLayout(LayoutKind.Sequential)] - internal readonly struct MONITORINFO - { - public readonly DWORD cbSize; - public readonly RECT rcMonitor; - public readonly RECT rcWork; - public readonly MONITOR_INFO_FLAGS dwFlags; - - public MONITORINFO(DWORD cbSize, RECT rcMonitor, RECT rcWork, MONITOR_INFO_FLAGS dwFlags) - { - this.cbSize = cbSize; - this.rcMonitor = rcMonitor; - this.rcWork = rcWork; - this.dwFlags = dwFlags; - } - - public static int Size => - Marshal.SizeOf(typeof(MONITORINFO)); - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs deleted file mode 100644 index 61f15c2944..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; - -namespace MouseJump.Common.NativeMethods; - -[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] -internal static partial class User32 -{ - /// - /// Determines the function's return value if the point is not contained within any display monitor. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint - /// - internal enum MONITOR_FROM_FLAGS : uint - { - MONITOR_DEFAULTTONULL = 0x00000000, - MONITOR_DEFAULTTOPRIMARY = 0x00000001, - MONITOR_DEFAULTTONEAREST = 0x00000002, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs deleted file mode 100644 index c7bbe4033b..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; - -namespace MouseJump.Common.NativeMethods; - -[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] -internal static partial class User32 -{ - /// - /// A set of flags that represent attributes of the display monitor. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo - /// - internal enum MONITOR_INFO_FLAGS : uint - { - MONITORINFOF_PRIMARY = 1, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs deleted file mode 100644 index 04eb36a312..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// The MonitorFromPoint function retrieves a handle to the display monitor that contains a specified point. - /// - /// - /// If the point is contained by a display monitor, the return value is an HMONITOR handle to that display monitor. - /// If the point is not contained by a display monitor, the return value depends on the value of dwFlags. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint - /// - [LibraryImport(Libraries.User32)] - internal static partial HMONITOR MonitorFromPoint( - POINT pt, - MONITOR_FROM_FLAGS dwFlags); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs deleted file mode 100644 index 6fa39e023d..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// The ReleaseDC function releases a device context (DC), freeing it for use by other - /// applications. The effect of the ReleaseDC function depends on the type of DC. It - /// frees only common and window DCs. It has no effect on class or private DCs. - /// - /// - /// The return value indicates whether the DC was released. If the DC was released, the return value is 1. - /// If the DC was not released, the return value is zero. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc - /// - [LibraryImport(Libraries.User32)] - internal static partial int ReleaseDC( - HWND hWnd, - HDC hDC); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs deleted file mode 100644 index 84b43227f7..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Contains information about a simulated message generated by an input device - /// other than a keyboard or mouse. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] - [StructLayout(LayoutKind.Sequential)] - internal readonly struct HARDWAREINPUT - { - public readonly DWORD uMsg; - public readonly WORD wParamL; - public readonly WORD wParamH; - - public HARDWAREINPUT( - DWORD uMsg, - WORD wParamL, - WORD wParamH) - { - this.uMsg = uMsg; - this.wParamL = wParamL; - this.wParamH = wParamH; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs deleted file mode 100644 index 543e7d8e18..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] - [StructLayout(LayoutKind.Sequential)] - internal readonly struct INPUT - { - public readonly INPUT_TYPE type; - public readonly DUMMYUNIONNAME data; - - public INPUT(INPUT_TYPE type, DUMMYUNIONNAME data) - { - this.type = type; - this.data = data; - } - - public static int Size => - Marshal.SizeOf(typeof(INPUT)); - - [StructLayout(LayoutKind.Explicit)] - public readonly struct DUMMYUNIONNAME - { - [FieldOffset(0)] - public readonly MOUSEINPUT mi; - [FieldOffset(0)] - public readonly KEYBDINPUT ki; - [FieldOffset(0)] - public readonly HARDWAREINPUT hi; - - public DUMMYUNIONNAME(MOUSEINPUT mi) - { - this.mi = mi; - } - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs deleted file mode 100644 index 8fd5a8ad20..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; - -namespace MouseJump.Common.NativeMethods; - -[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] -internal static partial class User32 -{ - /// - /// The type of the input event. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input - /// - internal enum INPUT_TYPE : uint - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs deleted file mode 100644 index 052836d5ef..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Contains information about a simulated keyboard event. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] - [StructLayout(LayoutKind.Sequential)] - internal readonly struct KEYBDINPUT - { - public readonly WORD wVk; - public readonly WORD wScan; - public readonly DWORD dwFlags; - public readonly DWORD time; - public readonly ULONG_PTR dwExtraInfo; - - public KEYBDINPUT( - WORD wVk, - WORD wScan, - DWORD dwFlags, - DWORD time, - ULONG_PTR dwExtraInfo) - { - this.wVk = wVk; - this.wScan = wScan; - this.dwFlags = dwFlags; - this.time = time; - this.dwExtraInfo = dwExtraInfo; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs deleted file mode 100644 index 5730ff9dc5..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs +++ /dev/null @@ -1,70 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput - /// - internal readonly struct LPINPUT - { - public static readonly LPINPUT Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public LPINPUT(IntPtr value) - { - this.Value = value; - } - - public LPINPUT(INPUT[] values) - { - this.Value = LPINPUT.ToPtr(values); - } - - public INPUT ToStructure() - { - return Marshal.PtrToStructure(this.Value); - } - - public IEnumerable ToStructure(int count) - { - var ptr = this.Value; - var size = INPUT.Size; - for (var i = 0; i < count; i++) - { - yield return Marshal.PtrToStructure(this.Value); - ptr += size; - } - } - - private static IntPtr ToPtr(INPUT[] values) - { - var mem = Marshal.AllocHGlobal(INPUT.Size * values.Length); - var ptr = mem; - var size = INPUT.Size; - foreach (var value in values) - { - Marshal.StructureToPtr(value, ptr, false); - ptr += size; - } - - return mem; - } - - public void Free() - { - Marshal.FreeHGlobal(this.Value); - } - - public override string ToString() - { - return $"{this.GetType().Name}({this.Value})"; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs deleted file mode 100644 index be243b778f..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Contains information about a simulated mouse event. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput - /// - [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] - [StructLayout(LayoutKind.Sequential)] - internal readonly struct MOUSEINPUT - { - public readonly int dx; - public readonly int dy; - public readonly DWORD mouseData; - public readonly MOUSE_EVENT_FLAGS dwFlags; - public readonly DWORD time; - public readonly ULONG_PTR dwExtraInfo; - - public MOUSEINPUT( - int dx, - int dy, - DWORD mouseData, - MOUSE_EVENT_FLAGS dwFlags, - DWORD time, - ULONG_PTR dwExtraInfo) - { - this.dx = dx; - this.dy = dy; - this.mouseData = mouseData; - this.dwFlags = dwFlags; - this.time = time; - this.dwExtraInfo = dwExtraInfo; - } - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs deleted file mode 100644 index 08b0780327..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; - -namespace MouseJump.Common.NativeMethods; - -[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] -internal static partial class User32 -{ - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput - /// - [Flags] - internal enum MOUSE_EVENT_FLAGS : uint - { - MOUSEEVENTF_MOVE = 0x0001, - MOUSEEVENTF_LEFTDOWN = 0x0002, - MOUSEEVENTF_LEFTUP = 0x0004, - MOUSEEVENTF_RIGHTDOWN = 0x0008, - MOUSEEVENTF_RIGHTUP = 0x0010, - MOUSEEVENTF_MIDDLEDOWN = 0x0020, - MOUSEEVENTF_MIDDLEUP = 0x0040, - MOUSEEVENTF_XDOWN = 0x0080, - MOUSEEVENTF_XUP = 0x0100, - MOUSEEVENTF_WHEEL = 0x0800, - MOUSEEVENTF_HWHEEL = 0x1000, - MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, - MOUSEEVENTF_VIRTUALDESK = 0x4000, - MOUSEEVENTF_ABSOLUTE = 0x8000, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs deleted file mode 100644 index 74eb5ac9fb..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Synthesizes keystrokes, mouse motions, and button clicks. - /// - /// - /// The function returns the number of events that it successfully inserted into the keyboard or mouse input stream. - /// If the function returns zero, the input was already blocked by another thread. - /// To get extended error information, call GetLastError. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput - /// - [LibraryImport(Libraries.User32, SetLastError = true)] - internal static partial UINT SendInput( - UINT cInputs, - LPINPUT pInputs, - int cbSize); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs deleted file mode 100644 index 1d84e17e2a..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Retrieves the position of the mouse cursor, in screen coordinates. - /// - /// - /// Returns nonzero if successful or zero otherwise. - /// To get extended error information, call GetLastError. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos - /// - [LibraryImport(Libraries.User32, SetLastError = true)] - internal static partial BOOL GetCursorPos( - LPPOINT lpPoint); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs deleted file mode 100644 index 8984e9639c..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Retrieves a handle to the desktop window. The desktop window covers the entire - /// screen. The desktop window is the area on top of which other windows are painted. - /// - /// - /// The return value is a handle to the desktop window. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow - /// - [LibraryImport(Libraries.User32)] - internal static partial HWND GetDesktopWindow(); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs deleted file mode 100644 index dafa5daadd..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Retrieves the specified system metric or system configuration setting. - /// - /// Note that all dimensions retrieved by GetSystemMetrics are in pixels. - /// - /// - /// If the function succeeds, the return value is the requested system metric or configuration setting. - /// If the function fails, the return value is 0. GetLastError does not provide extended error information. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics - /// - [LibraryImport(Libraries.User32)] - internal static partial int GetSystemMetrics( - SYSTEM_METRICS_INDEX smIndex); -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs deleted file mode 100644 index 8a714ad052..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs +++ /dev/null @@ -1,110 +0,0 @@ -// 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 System.Diagnostics.CodeAnalysis; - -namespace MouseJump.Common.NativeMethods; - -[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] -internal static partial class User32 -{ - internal enum SYSTEM_METRICS_INDEX : uint - { - SM_ARRANGE = 56, - SM_CLEANBOOT = 67, - SM_CMONITORS = 80, - SM_CMOUSEBUTTONS = 43, - SM_CONVERTIBLESLATEMODE = 0x2003, - SM_CXBORDER = 5, - SM_CXCURSOR = 13, - SM_CXDLGFRAME = 7, - SM_CXDOUBLECLK = 36, - SM_CXDRAG = 68, - SM_CXEDGE = 45, - SM_CXFIXEDFRAME = SM_CXDLGFRAME, - SM_CXFOCUSBORDER = 83, - SM_CXFRAME = 32, - SM_CXFULLSCREEN = 16, - SM_CXHSCROLL = 21, - SM_CXHTHUMB = 10, - SM_CXICON = 11, - SM_CXICONSPACING = 38, - SM_CXMAXIMIZED = 61, - SM_CXMAXTRACK = 59, - SM_CXMENUCHECK = 71, - SM_CXMENUSIZE = 54, - SM_CXMIN = 28, - SM_CXMINIMIZED = 57, - SM_CXMINSPACING = 47, - SM_CXMINTRACK = 34, - SM_CXPADDEDBORDER = 92, - SM_CXSCREEN = 0, - SM_CXSIZE = 30, - SM_CXSIZEFRAME = SM_CXFRAME, - SM_CXSMICON = 49, - SM_CXSMSIZE = 52, - SM_CXVIRTUALSCREEN = 78, - SM_CXVSCROLL = 2, - SM_CYBORDER = 6, - SM_CYCAPTION = 4, - SM_CYCURSOR = 14, - SM_CYDLGFRAME = 8, - SM_CYDOUBLECLK = 37, - SM_CYDRAG = 69, - SM_CYEDGE = 46, - SM_CYFIXEDFRAME = SM_CYDLGFRAME, - SM_CYFOCUSBORDER = 84, - SM_CYFRAME = 33, - SM_CYFULLSCREEN = 17, - SM_CYHSCROLL = 3, - SM_CYICON = 12, - SM_CYICONSPACING = 39, - SM_CYKANJIWINDOW = 18, - SM_CYMAXIMIZED = 62, - SM_CYMAXTRACK = 60, - SM_CYMENU = 15, - SM_CYMENUCHECK = 72, - SM_CYMENUSIZE = 55, - SM_CYMIN = 29, - SM_CYMINIMIZED = 58, - SM_CYMINSPACING = 48, - SM_CYMINTRACK = 35, - SM_CYSCREEN = 1, - SM_CYSIZE = 31, - SM_CYSIZEFRAME = SM_CYFRAME, - SM_CYSMCAPTION = 51, - SM_CYSMICON = 50, - SM_CYSMSIZE = 53, - SM_CYVIRTUALSCREEN = 79, - SM_CYVSCROLL = 20, - SM_CYVTHUMB = 9, - SM_DBCSENABLED = 42, - SM_DEBUG = 22, - SM_DIGITIZER = 94, - SM_IMMENABLED = 82, - SM_MAXIMUMTOUCHES = 95, - SM_MEDIACENTER = 87, - SM_MENUDROPALIGNMENT = 40, - SM_MIDEASTENABLED = 74, - SM_MOUSEPRESENT = 19, - SM_MOUSEHORIZONTALWHEELPRESENT = 91, - SM_MOUSEWHEELPRESENT = 75, - SM_NETWORK = 63, - SM_PENWINDOWS = 41, - SM_REMOTECONTROL = 0x2001, - SM_REMOTESESSION = 0x1000, - SM_SAMEDISPLAYFORMA = 81, - SM_SECURE = 44, - SM_SERVERR2 = 89, - SM_SHOWSOUNDS = 70, - SM_SHUTTINGDOWN = 0x2000, - SM_SLOWMACHINE = 73, - SM_STARTER = 88, - SM_SWAPBUTTON = 23, - SM_SYSTEMDOCKED = 0x2004, - SM_TABLETPC = 86, - SM_XVIRTUALSCREEN = 76, - SM_YVIRTUALSCREEN = 77, - } -} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs deleted file mode 100644 index b3bb04fef9..0000000000 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 System.Runtime.InteropServices; - -using static MouseJump.Common.NativeMethods.Core; - -namespace MouseJump.Common.NativeMethods; - -internal static partial class User32 -{ - /// - /// Moves the cursor to the specified screen coordinates. If the new coordinates are not within - /// the screen rectangle set by the most recent ClipCursor function call, the system automatically - /// adjusts the coordinates so that the cursor stays within the rectangle. - /// - /// - /// Returns nonzero if successful or zero otherwise. - /// To get extended error information, call GetLastError. - /// - /// - /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos - /// - [LibraryImport(Libraries.User32, SetLastError = true)] - internal static partial BOOL SetCursorPos( - int X, - int Y); -} diff --git a/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/KeystrokeTests.cs b/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/KeystrokeTests.cs new file mode 100644 index 0000000000..e1b234d92e --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/KeystrokeTests.cs @@ -0,0 +1,61 @@ +// 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 Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MouseJump.HotKeys.UnitTests; + +public static class KeystrokeTests +{ + [TestClass] + public sealed class ParseTests + { + public sealed class TestCase + { + public TestCase(string value, Keystroke expectedResult) + { + this.Value = value; + this.ExpectedResult = expectedResult; + } + + public string Value { get; } + + public Keystroke ExpectedResult { get; } + } + + public static IEnumerable GetTestCases() + { + // individual modifiers / keys + yield return new object[] { new TestCase("CTRL", new(Keys.None, KeyModifiers.Control)) }; + yield return new object[] { new TestCase("ALT", new(Keys.None, KeyModifiers.Alt)) }; + yield return new object[] { new TestCase("SHIFT", new(Keys.None, KeyModifiers.Shift)) }; + yield return new object[] { new TestCase("WIN", new(Keys.None, KeyModifiers.Windows)) }; + yield return new object[] { new TestCase("F", new(Keys.F, KeyModifiers.None)) }; + + // multiple modifiers + yield return new object[] { new TestCase("CTRL + ALT + SHIFT", new(Keys.None, KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) }; + yield return new object[] { new TestCase("SHIFT + ALT + CTRL", new(Keys.None, KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) }; + + // modifiers and keys + yield return new object[] { new TestCase("CTRL + ALT + SHIFT + F", new(Keys.F, KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift)) }; + yield return new object[] { new TestCase("CTRL + ALT + SHIFT + WIN + F", new(Keys.F, KeyModifiers.Control | KeyModifiers.Alt | KeyModifiers.Shift | KeyModifiers.Windows)) }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases))] + public void RunTestCases(TestCase data) + { + var expected = data.ExpectedResult; + var result = Keystroke.TryParse(data.Value, out var actual); + Assert.IsTrue(result); + if (actual == null) + { + throw new InvalidOperationException(); + } + + Assert.AreEqual(expected.Key, actual.Key); + Assert.AreEqual(expected.Modifiers, actual.Modifiers); + } + } +} diff --git a/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/MouseJump.HotKeys.UnitTests.csproj b/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/MouseJump.HotKeys.UnitTests.csproj new file mode 100644 index 0000000000..6282d1d6af --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys.UnitTests/MouseJump.HotKeys.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + + + {C1434F2F-D34A-4E4E-9471-4041066B8EC4} + PowerToys.MouseJump.HotKeys.UnitTests + PowerToys.MouseJump.HotKeys.UnitTests + PowerToys MouseJump.HotKeys.UnitTests + Library + ..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJump.HotKeys.UnitTests\ + false + false + + enable + enable + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.HotKeys/KeyModifiers.cs b/src/modules/MouseUtils/MouseJump.HotKeys/KeyModifiers.cs new file mode 100644 index 0000000000..ae43dc8901 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys/KeyModifiers.cs @@ -0,0 +1,18 @@ +// 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 Windows.Win32.UI.Input.KeyboardAndMouse; + +namespace MouseJump.HotKeys; + +[Flags] +public enum KeyModifiers +{ + None = 0, + Alt = (int)HOT_KEY_MODIFIERS.MOD_ALT, + Control = (int)HOT_KEY_MODIFIERS.MOD_CONTROL, + Shift = (int)HOT_KEY_MODIFIERS.MOD_SHIFT, + Windows = (int)HOT_KEY_MODIFIERS.MOD_WIN, + NoRepeat = (int)HOT_KEY_MODIFIERS.MOD_NOREPEAT, +} diff --git a/src/modules/MouseUtils/MouseJump.HotKeys/Keys.cs b/src/modules/MouseUtils/MouseJump.HotKeys/Keys.cs new file mode 100644 index 0000000000..e70944526b --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys/Keys.cs @@ -0,0 +1,1005 @@ +// 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. + +#pragma warning disable SA1512 // SingleLineCommentsMustNotBeFollowedByBlankLine +#pragma warning disable SA1515 // SingleLineCommentMustBePrecededByBlankLine +#pragma warning disable SA1636 // FileHeaderCopyrightTextMustMatch +// copied from https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms/src/System/Windows/Forms/Keys.cs +// so we don't need a reference to System.Windows.Forms in this project + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma warning disable SA1636 // FileHeaderCopyrightTextMustMatch +#pragma warning restore SA1515 // SingleLineCommentMustBePrecededByBlankLine +#pragma warning disable SA1512 // SingleLineCommentsMustNotBeFollowedByBlankLine + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJump.HotKeys; + +/// +/// Specifies key codes and modifiers. +/// +[Flags] +[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Names and values taken from Win32Api")] +[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Names and values taken from Win32Api")] +public enum Keys +{ + /// + /// The bit mask to extract a key code from a key value. + /// + KeyCode = 0x0000FFFF, + + /// + /// The bit mask to extract modifiers from a key value. + /// + Modifiers = unchecked((int)0xFFFF0000), + + /// + /// No key pressed. + /// + None = 0x00, + + /// + /// The left mouse button. + /// + LButton = 0x01, + + /// + /// The right mouse button. + /// + RButton = 0x02, + + /// + /// The CANCEL key. + /// + Cancel = 0x03, + + /// + /// The middle mouse button (three-button mouse). + /// + MButton = 0x04, + + /// + /// The first x mouse button (five-button mouse). + /// + XButton1 = 0x05, + + /// + /// The second x mouse button (five-button mouse). + /// + XButton2 = 0x06, + + /// + /// The BACKSPACE key. + /// + Back = 0x08, + + /// + /// The TAB key. + /// + Tab = 0x09, + + /// + /// The CLEAR key. + /// + LineFeed = 0x0A, + + /// + /// The CLEAR key. + /// + Clear = 0x0C, + + /// + /// The RETURN key. + /// + Return = 0x0D, + + /// + /// The ENTER key. + /// + Enter = Return, + + /// + /// The SHIFT key. + /// + ShiftKey = 0x10, + + /// + /// The CTRL key. + /// + ControlKey = 0x11, + + /// + /// The ALT key. + /// + Menu = 0x12, + + /// + /// The PAUSE key. + /// + Pause = 0x13, + + /// + /// The CAPS LOCK key. + /// + Capital = 0x14, + + /// + /// The CAPS LOCK key. + /// + [SuppressMessage("Naming", "CA1069", Justification = "Name and value taken from Win32Api")] + CapsLock = 0x14, + + /// + /// The IME Kana mode key. + /// + KanaMode = 0x15, + + /// + /// The IME Hanguel mode key. + /// + [SuppressMessage("Naming", "CA1069", Justification = "Name and value taken from Win32Api")] + HanguelMode = 0x15, + + /// + /// The IME Hangul mode key. + /// + [SuppressMessage("Naming", "CA1069", Justification = "Name and value taken from Win32Api")] + HangulMode = 0x15, + + /// + /// The IME Junja mode key. + /// + JunjaMode = 0x17, + + /// + /// The IME Final mode key. + /// + FinalMode = 0x18, + + /// + /// The IME Hanja mode key. + /// + HanjaMode = 0x19, + + /// + /// The IME Kanji mode key. + /// + [SuppressMessage("Naming", "CA1069", Justification = "Name and value taken from Win32Api")] + KanjiMode = 0x19, + + /// + /// The ESC key. + /// + Escape = 0x1B, + + /// + /// The IME Convert key. + /// + IMEConvert = 0x1C, + + /// + /// The IME NonConvert key. + /// + IMENonconvert = 0x1D, + + /// + /// The IME Accept key. + /// + IMEAccept = 0x1E, + + /// + /// The IME Accept key. + /// + IMEAceept = IMEAccept, + + /// + /// The IME Mode change request. + /// + IMEModeChange = 0x1F, + + /// + /// The SPACEBAR key. + /// + Space = 0x20, + + /// + /// The PAGE UP key. + /// + Prior = 0x21, + + /// + /// The PAGE UP key. + /// + PageUp = Prior, + + /// + /// The PAGE DOWN key. + /// + Next = 0x22, + + /// + /// The PAGE DOWN key. + /// + PageDown = Next, + + /// + /// The END key. + /// + End = 0x23, + + /// + /// The HOME key. + /// + Home = 0x24, + + /// + /// The LEFT ARROW key. + /// + Left = 0x25, + + /// + /// The UP ARROW key. + /// + Up = 0x26, + + /// + /// The RIGHT ARROW key. + /// + Right = 0x27, + + /// + /// The DOWN ARROW key. + /// + Down = 0x28, + + /// + /// The SELECT key. + /// + Select = 0x29, + + /// + /// The PRINT key. + /// + Print = 0x2A, + + /// + /// The EXECUTE key. + /// + Execute = 0x2B, + + /// + /// The PRINT SCREEN key. + /// + Snapshot = 0x2C, + + /// + /// The PRINT SCREEN key. + /// + PrintScreen = Snapshot, + + /// + /// The INS key. + /// + Insert = 0x2D, + + /// + /// The DEL key. + /// + Delete = 0x2E, + + /// + /// The HELP key. + /// + Help = 0x2F, + + /// + /// The 0 key. + /// + D0 = 0x30, // 0 + + /// + /// The 1 key. + /// + D1 = 0x31, // 1 + + /// + /// The 2 key. + /// + D2 = 0x32, // 2 + + /// + /// The 3 key. + /// + D3 = 0x33, // 3 + + /// + /// The 4 key. + /// + D4 = 0x34, // 4 + + /// + /// The 5 key. + /// + D5 = 0x35, // 5 + + /// + /// The 6 key. + /// + D6 = 0x36, // 6 + + /// + /// The 7 key. + /// + D7 = 0x37, // 7 + + /// + /// The 8 key. + /// + D8 = 0x38, // 8 + + /// + /// The 9 key. + /// + D9 = 0x39, // 9 + + /// + /// The A key. + /// + A = 0x41, + + /// + /// The B key. + /// + B = 0x42, + + /// + /// The C key. + /// + C = 0x43, + + /// + /// The D key. + /// + D = 0x44, + + /// + /// The E key. + /// + E = 0x45, + + /// + /// The F key. + /// + F = 0x46, + + /// + /// The G key. + /// + G = 0x47, + + /// + /// The H key. + /// + H = 0x48, + + /// + /// The I key. + /// + I = 0x49, + + /// + /// The J key. + /// + J = 0x4A, + + /// + /// The K key. + /// + K = 0x4B, + + /// + /// The L key. + /// + L = 0x4C, + + /// + /// The M key. + /// + M = 0x4D, + + /// + /// The N key. + /// + N = 0x4E, + + /// + /// The O key. + /// + O = 0x4F, + + /// + /// The P key. + /// + P = 0x50, + + /// + /// The Q key. + /// + Q = 0x51, + + /// + /// The R key. + /// + R = 0x52, + + /// + /// The S key. + /// + S = 0x53, + + /// + /// The T key. + /// + T = 0x54, + + /// + /// The U key. + /// + U = 0x55, + + /// + /// The V key. + /// + V = 0x56, + + /// + /// The W key. + /// + W = 0x57, + + /// + /// The X key. + /// + X = 0x58, + + /// + /// The Y key. + /// + Y = 0x59, + + /// + /// The Z key. + /// + Z = 0x5A, + + /// + /// The left Windows logo key (Microsoft Natural Keyboard). + /// + LWin = 0x5B, + + /// + /// The right Windows logo key (Microsoft Natural Keyboard). + /// + RWin = 0x5C, + + /// + /// The Application key (Microsoft Natural Keyboard). + /// + Apps = 0x5D, + + /// + /// The Computer Sleep key. + /// + Sleep = 0x5F, + + /// + /// The 0 key on the numeric keypad. + /// + NumPad0 = 0x60, + + /// + /// The 1 key on the numeric keypad. + /// + NumPad1 = 0x61, + + /// + /// The 2 key on the numeric keypad. + /// + NumPad2 = 0x62, + + /// + /// The 3 key on the numeric keypad. + /// + NumPad3 = 0x63, + + /// + /// The 4 key on the numeric keypad. + /// + NumPad4 = 0x64, + + /// + /// The 5 key on the numeric keypad. + /// + NumPad5 = 0x65, + + /// + /// The 6 key on the numeric keypad. + /// + NumPad6 = 0x66, + + /// + /// The 7 key on the numeric keypad. + /// + NumPad7 = 0x67, + + /// + /// The 8 key on the numeric keypad. + /// + NumPad8 = 0x68, + + /// + /// The 9 key on the numeric keypad. + /// + NumPad9 = 0x69, + + /// + /// The Multiply key. + /// + Multiply = 0x6A, + + /// + /// The Add key. + /// + Add = 0x6B, + + /// + /// The Separator key. + /// + Separator = 0x6C, + + /// + /// The Subtract key. + /// + Subtract = 0x6D, + + /// + /// The Decimal key. + /// + [SuppressMessage("Naming", "CA1720", Justification = "Name and value taken from Win32Api")] + Decimal = 0x6E, + + /// + /// The Divide key. + /// + Divide = 0x6F, + + /// + /// The F1 key. + /// + F1 = 0x70, + + /// + /// The F2 key. + /// + F2 = 0x71, + + /// + /// The F3 key. + /// + F3 = 0x72, + + /// + /// The F4 key. + /// + F4 = 0x73, + + /// + /// The F5 key. + /// + F5 = 0x74, + + /// + /// The F6 key. + /// + F6 = 0x75, + + /// + /// The F7 key. + /// + F7 = 0x76, + + /// + /// The F8 key. + /// + F8 = 0x77, + + /// + /// The F9 key. + /// + F9 = 0x78, + + /// + /// The F10 key. + /// + F10 = 0x79, + + /// + /// The F11 key. + /// + F11 = 0x7A, + + /// + /// The F12 key. + /// + F12 = 0x7B, + + /// + /// The F13 key. + /// + F13 = 0x7C, + + /// + /// The F14 key. + /// + F14 = 0x7D, + + /// + /// The F15 key. + /// + F15 = 0x7E, + + /// + /// The F16 key. + /// + F16 = 0x7F, + + /// + /// The F17 key. + /// + F17 = 0x80, + + /// + /// The F18 key. + /// + F18 = 0x81, + + /// + /// The F19 key. + /// + F19 = 0x82, + + /// + /// The F20 key. + /// + F20 = 0x83, + + /// + /// The F21 key. + /// + F21 = 0x84, + + /// + /// The F22 key. + /// + F22 = 0x85, + + /// + /// The F23 key. + /// + F23 = 0x86, + + /// + /// The F24 key. + /// + F24 = 0x87, + + /// + /// The NUM LOCK key. + /// + NumLock = 0x90, + + /// + /// The SCROLL LOCK key. + /// + Scroll = 0x91, + + /// + /// The left SHIFT key. + /// + LShiftKey = 0xA0, + + /// + /// The right SHIFT key. + /// + RShiftKey = 0xA1, + + /// + /// The left CTRL key. + /// + LControlKey = 0xA2, + + /// + /// The right CTRL key. + /// + RControlKey = 0xA3, + + /// + /// The left ALT key. + /// + LMenu = 0xA4, + + /// + /// The right ALT key. + /// + RMenu = 0xA5, + + /// + /// The Browser Back key. + /// + BrowserBack = 0xA6, + + /// + /// The Browser Forward key. + /// + BrowserForward = 0xA7, + + /// + /// The Browser Refresh key. + /// + BrowserRefresh = 0xA8, + + /// + /// The Browser Stop key. + /// + BrowserStop = 0xA9, + + /// + /// The Browser Search key. + /// + BrowserSearch = 0xAA, + + /// + /// The Browser Favorites key. + /// + BrowserFavorites = 0xAB, + + /// + /// The Browser Home key. + /// + BrowserHome = 0xAC, + + /// + /// The Volume Mute key. + /// + VolumeMute = 0xAD, + + /// + /// The Volume Down key. + /// + VolumeDown = 0xAE, + + /// + /// The Volume Up key. + /// + VolumeUp = 0xAF, + + /// + /// The Media Next Track key. + /// + MediaNextTrack = 0xB0, + + /// + /// The Media Previous Track key. + /// + MediaPreviousTrack = 0xB1, + + /// + /// The Media Stop key. + /// + MediaStop = 0xB2, + + /// + /// The Media Play Pause key. + /// + MediaPlayPause = 0xB3, + + /// + /// The Launch Mail key. + /// + LaunchMail = 0xB4, + + /// + /// The Select Media key. + /// + SelectMedia = 0xB5, + + /// + /// The Launch Application1 key. + /// + LaunchApplication1 = 0xB6, + + /// + /// The Launch Application2 key. + /// + LaunchApplication2 = 0xB7, + + /// + /// The Oem Semicolon key. + /// + OemSemicolon = 0xBA, + + /// + /// The Oem 1 key. + /// + Oem1 = OemSemicolon, + + /// + /// The Oem plus key. + /// + Oemplus = 0xBB, + + /// + /// The Oem comma key. + /// + Oemcomma = 0xBC, + + /// + /// The Oem Minus key. + /// + OemMinus = 0xBD, + + /// + /// The Oem Period key. + /// + OemPeriod = 0xBE, + + /// + /// The Oem Question key. + /// + OemQuestion = 0xBF, + + /// + /// The Oem 2 key. + /// + Oem2 = OemQuestion, + + /// + /// The Oem tilde key. + /// + Oemtilde = 0xC0, + + /// + /// The Oem 3 key. + /// + Oem3 = Oemtilde, + + /// + /// The Oem Open Brackets key. + /// + OemOpenBrackets = 0xDB, + + /// + /// The Oem 4 key. + /// + Oem4 = OemOpenBrackets, + + /// + /// The Oem Pipe key. + /// + OemPipe = 0xDC, + + /// + /// The Oem 5 key. + /// + Oem5 = OemPipe, + + /// + /// The Oem Close Brackets key. + /// + OemCloseBrackets = 0xDD, + + /// + /// The Oem 6 key. + /// + Oem6 = OemCloseBrackets, + + /// + /// The Oem Quotes key. + /// + OemQuotes = 0xDE, + + /// + /// The Oem 7 key. + /// + Oem7 = OemQuotes, + + /// + /// The Oem8 key. + /// + Oem8 = 0xDF, + + /// + /// The Oem Backslash key. + /// + OemBackslash = 0xE2, + + /// + /// The Oem 102 key. + /// + Oem102 = OemBackslash, + + /// + /// The PROCESS KEY key. + /// + ProcessKey = 0xE5, + + /// + /// The Packet KEY key. + /// + Packet = 0xE7, + + /// + /// The ATTN key. + /// + Attn = 0xF6, + + /// + /// The CRSEL key. + /// + Crsel = 0xF7, + + /// + /// The EXSEL key. + /// + Exsel = 0xF8, + + /// + /// The ERASE EOF key. + /// + EraseEof = 0xF9, + + /// + /// The PLAY key. + /// + Play = 0xFA, + + /// + /// The ZOOM key. + /// + Zoom = 0xFB, + + /// + /// A constant reserved for future use. + /// + NoName = 0xFC, + + /// + /// The PA1 key. + /// + Pa1 = 0xFD, + + /// + /// The CLEAR key. + /// + OemClear = 0xFE, + + /// + /// The SHIFT modifier key. + /// + Shift = 0x00010000, + + /// + /// The CTRL modifier key. + /// + Control = 0x00020000, + + /// + /// The ALT modifier key. + /// + Alt = 0x00040000, +} diff --git a/src/modules/MouseUtils/MouseJump.HotKeys/Keystroke.cs b/src/modules/MouseUtils/MouseJump.HotKeys/Keystroke.cs new file mode 100644 index 0000000000..0ac6e20838 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys/Keystroke.cs @@ -0,0 +1,120 @@ +// 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 System.Diagnostics.CodeAnalysis; + +namespace MouseJump.HotKeys; + +public sealed class Keystroke +{ + public Keystroke(Keys key, KeyModifiers modifiers) + { + this.Key = key; + this.Modifiers = modifiers; + } + + public Keys Key + { + get; + } + + public KeyModifiers Modifiers + { + get; + } + + public static Keystroke Parse(string s) + { + if (!Keystroke.TryParse(s, out var result)) + { + throw new ArgumentException("Invalid argument format.", nameof(s)); + } + + return result; + } + + public static bool TryParse(string s, [NotNullWhen(true)] out Keystroke? result) + { + // see https://github.com/microsoft/terminal/blob/14919073a12fc0ecb4a9805cc183fdd68d30c4b6/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp#L124 + // for an alternate implementation + + // e.g. "CTRL + ALT + SHIFT + F" + if (string.IsNullOrEmpty(s)) + { + result = null; + return false; + } + + var parts = s + .Replace(" ", string.Empty) + .ToUpperInvariant() + .Split('+'); + + var keystroke = (Keys: Keys.None, Modifiers: KeyModifiers.None); + + foreach (var part in parts) + { + switch (part) + { + case "CTRL": + keystroke.Modifiers |= KeyModifiers.Control; + break; + case "ALT": + keystroke.Modifiers |= KeyModifiers.Alt; + break; + case "SHIFT": + keystroke.Modifiers |= KeyModifiers.Shift; + break; + case "WIN": + keystroke.Modifiers |= KeyModifiers.Windows; + break; + default: + if (!Enum.TryParse(part, out var key)) + { + result = null; + return false; + } + + keystroke.Keys = key; + break; + } + } + + result = new Keystroke( + keystroke.Keys, keystroke.Modifiers); + return true; + } + + public override string ToString() + { + var parts = new List(); + + if (this.Modifiers.HasFlag(KeyModifiers.Control)) + { + parts.Add("CTRL"); + } + + if (this.Modifiers.HasFlag(KeyModifiers.Alt)) + { + parts.Add("ALT"); + } + + if (this.Modifiers.HasFlag(KeyModifiers.Shift)) + { + parts.Add("SHIFT"); + } + + if (this.Modifiers.HasFlag(KeyModifiers.Windows)) + { + parts.Add("WIN"); + } + + if (this.Key != Keys.None) + { + parts.Add(this.Key.ToString()); + } + + return string.Join(" + ", parts); + } +} diff --git a/src/modules/MouseUtils/MouseJump.HotKeys/MouseJump.HotKeys.csproj b/src/modules/MouseUtils/MouseJump.HotKeys/MouseJump.HotKeys.csproj new file mode 100644 index 0000000000..e534d45d85 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys/MouseJump.HotKeys.csproj @@ -0,0 +1,31 @@ + + + + + + {A8D81CE0-7E24-4079-AD45-6BCE6747FE8B} + PowerToys.MouseJump.HotKeys + PowerToys.MouseJump.HotKeys + PowerToys MouseJump.HotKeys + Library + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps + false + false + + PerMonitorV2 + true + enable + enable + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/modules/MouseUtils/MouseJump.HotKeys/NativeMethods.txt b/src/modules/MouseUtils/MouseJump.HotKeys/NativeMethods.txt new file mode 100644 index 0000000000..7aa162ee12 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.HotKeys/NativeMethods.txt @@ -0,0 +1 @@ +HOT_KEY_MODIFIERS diff --git a/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/BoxBoundsTests.cs b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/BoxBoundsTests.cs new file mode 100644 index 0000000000..6f7b3a9183 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/BoxBoundsTests.cs @@ -0,0 +1,116 @@ +// 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 System.Drawing; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; + +namespace MouseJump.Models.UnitTests.Drawing; + +public static class BoxBoundsTests +{ + [TestClass] + public sealed class CreateFromContentBounds + { + public sealed class TestCase + { + public TestCase(RectangleInfo contentBounds, BoxStyle boxStyle, BoxBounds expectedResult) + { + this.ContentBounds = contentBounds; + this.BoxStyle = boxStyle; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo ContentBounds { get; } + + public BoxStyle BoxStyle { get; } + + public BoxBounds ExpectedResult { get; } + } + + public static IEnumerable GetTestCases() + { + yield return new object[] + { + new TestCase( + contentBounds: new(100, 100, 800, 600), + boxStyle: new( + marginStyle: new(3), + borderStyle: new(Color.Red, 5, 0), + paddingStyle: new(7), + backgroundStyle: BackgroundStyle.Empty), + expectedResult: new( + outerBounds: new(85, 85, 830, 630), + marginBounds: new(85, 85, 830, 630), + borderBounds: new(88, 88, 824, 624), + paddingBounds: new(93, 93, 814, 614), + contentBounds: new(100, 100, 800, 600))), + }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases))] + public void RunTestCases(TestCase data) + { + var actual = BoxBounds.CreateFromContentBounds(data.ContentBounds, data.BoxStyle); + var expected = data.ExpectedResult; + Assert.AreEqual( + JsonSerializer.Serialize(expected), + JsonSerializer.Serialize(actual)); + } + } + + [TestClass] + public sealed class CreateFromOuterBounds + { + public sealed class TestCase + { + public TestCase(RectangleInfo outerBounds, BoxStyle boxStyle, BoxBounds expectedResult) + { + this.OuterBounds = outerBounds; + this.BoxStyle = boxStyle; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo OuterBounds { get; } + + public BoxStyle BoxStyle { get; } + + public BoxBounds ExpectedResult { get; } + } + + public static IEnumerable GetTestCases() + { + yield return new object[] + { + new TestCase( + outerBounds: new(85, 85, 830, 630), + boxStyle: new( + marginStyle: new(3), + borderStyle: new(Color.Red, 5, 0), + paddingStyle: new(7), + backgroundStyle: BackgroundStyle.Empty), + expectedResult: new( + outerBounds: new(85, 85, 830, 630), + marginBounds: new(85, 85, 830, 630), + borderBounds: new(88, 88, 824, 624), + paddingBounds: new(93, 93, 814, 614), + contentBounds: new(100, 100, 800, 600))), + }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases))] + public void RunTestCases(TestCase data) + { + var actual = BoxBounds.CreateFromOuterBounds(data.OuterBounds, data.BoxStyle); + var expected = data.ExpectedResult; + Assert.AreEqual( + JsonSerializer.Serialize(expected), + JsonSerializer.Serialize(actual)); + } + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/RectangleInfoTests.cs similarity index 93% rename from src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs rename to src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/RectangleInfoTests.cs index d2d6aba81c..fb0fcb8a29 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs +++ b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/RectangleInfoTests.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJump.Common.Models.Drawing; +using MouseJump.Models.Drawing; -namespace MouseJump.Common.UnitTests.Models.Drawing; +namespace MouseJump.Models.UnitTests.Drawing; -[TestClass] public static class RectangleInfoTests { [TestClass] @@ -49,7 +48,7 @@ public static class RectangleInfoTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { var actual = data.Rectangle.Center(data.Point); @@ -62,7 +61,7 @@ public static class RectangleInfoTests } [TestClass] - public sealed class ClampTests + public sealed class MoveInsideTests { public sealed class TestCase { @@ -126,10 +125,10 @@ public static class RectangleInfoTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { - var actual = data.Inner.Clamp(data.Outer); + var actual = data.Inner.MoveInside(data.Outer); var expected = data.ExpectedResult; Assert.AreEqual(expected.X, actual.X); Assert.AreEqual(expected.Y, actual.Y); diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/SizeInfoTests.cs similarity index 93% rename from src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs rename to src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/SizeInfoTests.cs index 9871d0526c..6bf31a2124 100644 --- a/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs +++ b/src/modules/MouseUtils/MouseJump.Models.UnitTests/Drawing/SizeInfoTests.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJump.Common.Models.Drawing; +using MouseJump.Models.Drawing; -namespace MouseJump.Common.UnitTests.Models.Drawing; +namespace MouseJump.Models.UnitTests.Drawing; -[TestClass] public static class SizeInfoTests { [TestClass] @@ -50,7 +49,7 @@ public static class SizeInfoTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { var actual = data.Source.ScaleToFit(data.Bounds, out var scalingRatio); @@ -98,7 +97,7 @@ public static class SizeInfoTests } [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + [DynamicData(nameof(GetTestCases))] public void RunTestCases(TestCase data) { var actual = data.Obj.ScaleToFitRatio(data.Bounds); diff --git a/src/modules/MouseUtils/MouseJump.Models.UnitTests/MSTestSettings.cs b/src/modules/MouseUtils/MouseJump.Models.UnitTests/MSTestSettings.cs new file mode 100644 index 0000000000..8b659fd487 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models.UnitTests/MSTestSettings.cs @@ -0,0 +1,7 @@ +// 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 Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/src/modules/MouseUtils/MouseJump.Models.UnitTests/MouseJump.Models.UnitTests.csproj b/src/modules/MouseUtils/MouseJump.Models.UnitTests/MouseJump.Models.UnitTests.csproj new file mode 100644 index 0000000000..637445cf19 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models.UnitTests/MouseJump.Models.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + + + {AB6305DB-CA33-4EF4-BA40-B0CFC10E7B9F} + PowerToys.MouseJump.Models.UnitTests + PowerToys.MouseJump.Models.UnitTests + PowerToys MouseJump.Models.UnitTests + Library + ..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJump.Models.UnitTests\ + false + false + + enable + enable + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.Models/Display/DesktopInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Display/DesktopInfo.cs new file mode 100644 index 0000000000..89d1206957 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/Display/DesktopInfo.cs @@ -0,0 +1,32 @@ +// 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 System.Collections.ObjectModel; + +using MouseJump.Models.Drawing; + +namespace MouseJump.Models.Display; + +/// +/// Represents the entire desktop or virtual screen for a single logical device. +/// +public sealed record DesktopInfo +{ + public DesktopInfo(IEnumerable screens) + { + this.Screens = (screens ?? throw new ArgumentNullException(nameof(screens))).ToList().AsReadOnly(); + } + + public ReadOnlyCollection Screens + { + get; + } + + public RectangleInfo GetCombinedDisplayArea() + { + return (this.Screens.Count == 0) + ? throw new InvalidOperationException($"{nameof(GetCombinedDisplayArea)} requires one or more screens.") + : RectangleInfo.Union(this.Screens.Select(screen => screen.DisplayArea)); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/Display/DeviceInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Display/DeviceInfo.cs new file mode 100644 index 0000000000..0b02ed7605 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/Display/DeviceInfo.cs @@ -0,0 +1,42 @@ +// 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 System.Collections.ObjectModel; + +using MouseJump.Models.Drawing; + +namespace MouseJump.Models.Display; + +/// +/// Represents a device whose screens are rendered in the preview image. +/// +public sealed record DeviceInfo +{ + public DeviceInfo(string hostname, bool localhost, IEnumerable screens) + { + this.Hostname = hostname ?? throw new ArgumentNullException(nameof(hostname)); + this.Localhost = localhost; + this.Screens = (screens ?? throw new ArgumentNullException(nameof(screens))).ToList().AsReadOnly(); + } + + public string Hostname + { + get; + } + + public bool Localhost + { + get; + } + + public ReadOnlyCollection Screens + { + get; + } + + public RectangleInfo GetCombinedDisplayArea() + { + return RectangleInfo.Union(this.Screens.Select(screen => screen.DisplayArea)); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/Display/DisplayInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Display/DisplayInfo.cs new file mode 100644 index 0000000000..096fcc2428 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/Display/DisplayInfo.cs @@ -0,0 +1,20 @@ +// 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 System.Collections.ObjectModel; + +namespace MouseJump.Models.Display; + +public sealed record DisplayInfo +{ + public DisplayInfo(IEnumerable devices) + { + this.Devices = (devices ?? throw new ArgumentNullException(nameof(devices))).ToList().AsReadOnly(); + } + + public ReadOnlyCollection Devices + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/Display/HighDpiMode.cs b/src/modules/MouseUtils/MouseJump.Models/Display/HighDpiMode.cs new file mode 100644 index 0000000000..cef2da52ca --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/Display/HighDpiMode.cs @@ -0,0 +1,17 @@ +// 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. + +namespace MouseJump.Models.Display; + +/// +/// See https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/HighDpiMode.cs#L26 +/// +public enum HighDpiMode +{ + DpiUnaware, + SystemAware, + PerMonitor, + PerMonitorV2, + DpiUnawareGdiScaled, +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Display/ScreenInfo.cs similarity index 61% rename from src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs rename to src/modules/MouseUtils/MouseJump.Models/Display/ScreenInfo.cs index 6a7862236f..b7235d4c77 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Display/ScreenInfo.cs @@ -2,40 +2,49 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Drawing; +using System.Text.Json.Serialization; + +using MouseJump.Models.Drawing; + +namespace MouseJump.Models.Display; /// /// Immutable version of a System.Windows.Forms.Screen object so we don't need to /// take a dependency on WinForms just for screen info. /// -public sealed class ScreenInfo +public sealed record ScreenInfo { - public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) + public ScreenInfo(nint handle, bool primary, RectangleInfo displayArea, RectangleInfo? workingArea) { // this.Handle is a HMONITOR that has been cast to an int because we don't want // to expose the HMONITOR type outside the current assembly. this.Handle = handle; this.Primary = primary; this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea)); - this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea)); + this.WorkingArea = workingArea; } - public int Handle + [JsonPropertyName("handle")] + [JsonConverter(typeof(NintJsonConverter))] + public nint Handle { get; } + [JsonPropertyName("primary")] public bool Primary { get; } + [JsonPropertyName("displayArea")] public RectangleInfo DisplayArea { get; } - public RectangleInfo WorkingArea + [JsonPropertyName("workingArea")] + public RectangleInfo? WorkingArea { get; } diff --git a/src/modules/MouseUtils/MouseJump.Models/Drawing/BoxBounds.cs b/src/modules/MouseUtils/MouseJump.Models/Drawing/BoxBounds.cs new file mode 100644 index 0000000000..407a2e84b9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/Drawing/BoxBounds.cs @@ -0,0 +1,167 @@ +// 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 MouseJump.Models.Styles; + +namespace MouseJump.Models.Drawing; + +/// +/// Represents the bounds of a layout box, including outer, margin, border, padding, +/// and content bounds. This class is used to model the box layout as described +/// in the CSS box model. +/// +public sealed class BoxBounds +{ + /* + + see https://www.w3schools.com/css/css_boxmodel.asp + + +--------------[bounds]---------------+ + |▒▒▒▒▒▒▒▒▒▒▒▒▒▒[margin]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| + |▒▒▓▓▓▓▓▓▓▓▓▓▓▓[border]▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒| + |▒▒▓▓░░░░░░░░░░[padding]░░░░░░░░░░▓▓▒▒| + |▒▒▓▓░░ ░░▓▓▒▒| + |▒▒▓▓░░ ░░▓▓▒▒| + |▒▒▓▓░░ [content] ░░▓▓▒▒| + |▒▒▓▓░░ ░░▓▓▒▒| + |▒▒▓▓░░ ░░▓▓▒▒| + |▒▒▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▒▒| + |▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒| + |▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| + +-------------------------------------+ + + */ + + public static readonly BoxBounds Empty = new( + outerBounds: RectangleInfo.Empty, + marginBounds: RectangleInfo.Empty, + borderBounds: RectangleInfo.Empty, + paddingBounds: RectangleInfo.Empty, + contentBounds: RectangleInfo.Empty, + isEmpty: true); + + public BoxBounds( + RectangleInfo outerBounds, + RectangleInfo marginBounds, + RectangleInfo borderBounds, + RectangleInfo paddingBounds, + RectangleInfo contentBounds) + : this( + outerBounds, + marginBounds, + borderBounds, + paddingBounds, + contentBounds, + isEmpty: false) + { + } + + private BoxBounds( + RectangleInfo outerBounds, + RectangleInfo marginBounds, + RectangleInfo borderBounds, + RectangleInfo paddingBounds, + RectangleInfo contentBounds, + bool isEmpty) + { + this.OuterBounds = outerBounds ?? throw new ArgumentNullException(nameof(outerBounds)); + this.MarginBounds = marginBounds ?? throw new ArgumentNullException(nameof(marginBounds)); + this.BorderBounds = borderBounds ?? throw new ArgumentNullException(nameof(borderBounds)); + this.PaddingBounds = paddingBounds ?? throw new ArgumentNullException(nameof(paddingBounds)); + this.ContentBounds = contentBounds ?? throw new ArgumentNullException(nameof(contentBounds)); + this.IsEmpty = isEmpty; + } + + /// + /// Gets the outer bounds of this layout box. + /// + public RectangleInfo OuterBounds + { + get; + } + + public RectangleInfo MarginBounds + { + get; + } + + public RectangleInfo BorderBounds + { + get; + } + + public RectangleInfo PaddingBounds + { + get; + } + + /// + /// Gets the bounds of the content area for this layout box. + /// + public RectangleInfo ContentBounds + { + get; + } + + public bool IsEmpty + { + get; + } + + /// + /// Calculates the bounds of the various areas of a box, given the content bounds and the box style. + /// Starts with the content bounds and works outward, enlarging the content bounds by the padding, border, and margin sizes to calculate the outer bounds of the box. + /// + /// The content bounds of the box. + /// The style of the box, which includes the sizes of the margin, border, and padding areas. + /// A object that represents the bounds of the different areas of the box. + /// Thrown when or is null. + /// Thrown when any of the styles in is null. + public static BoxBounds CreateFromContentBounds( + RectangleInfo contentBounds, + BoxStyle boxStyle) + { + ArgumentNullException.ThrowIfNull(contentBounds); + ArgumentNullException.ThrowIfNull(boxStyle); + if (boxStyle.PaddingStyle == null || boxStyle.BorderStyle == null || boxStyle.MarginStyle == null) + { + throw new ArgumentException(null, nameof(boxStyle)); + } + + var paddingBounds = contentBounds.Enlarge(boxStyle.PaddingStyle); + var borderBounds = paddingBounds.Enlarge(boxStyle.BorderStyle); + var marginBounds = borderBounds.Enlarge(boxStyle.MarginStyle); + var outerBounds = marginBounds; + return new( + outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds); + } + + /// + /// Calculates the bounds of the various areas of a box, given the outer bounds and the box style. + /// This method starts with the outer bounds and works inward, shrinking the outer bounds by the margin, border, and padding sizes to calculate the content bounds of the box. + /// + /// The outer bounds of the box. + /// The style of the box, which includes the sizes of the margin, border, and padding areas. + /// A object that represents the bounds of the different areas of the box. + /// Thrown when or is null. + /// Thrown when any of the styles in is null. + public static BoxBounds CreateFromOuterBounds( + RectangleInfo outerBounds, + BoxStyle boxStyle) + { + ArgumentNullException.ThrowIfNull(outerBounds); + ArgumentNullException.ThrowIfNull(boxStyle); + if (outerBounds == null || boxStyle.MarginStyle == null || boxStyle.BorderStyle == null || boxStyle.PaddingStyle == null) + { + throw new ArgumentException(null, nameof(boxStyle)); + } + + var marginBounds = outerBounds; + var borderBounds = marginBounds.Shrink(boxStyle.MarginStyle); + var paddingBounds = borderBounds.Shrink(boxStyle.BorderStyle); + var contentBounds = paddingBounds.Shrink(boxStyle.PaddingStyle); + return new( + outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Drawing/PointInfo.cs similarity index 78% rename from src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs rename to src/modules/MouseUtils/MouseJump.Models/Drawing/PointInfo.cs index 676fdb92c0..36ee36d63f 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Drawing/PointInfo.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Drawing; +using System.Drawing; + +namespace MouseJump.Models.Drawing; /// /// Immutable version of a System.Drawing.Point object with some extra utility methods. @@ -40,9 +42,30 @@ public sealed class PointInfo y: Math.Clamp(this.Y, outer.Y, outer.Bottom)); } + public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y); + + public PointInfo Offset(SizeInfo amount) => new(this.X + amount.Width, this.Y + amount.Height); + + public PointInfo Offset(decimal amountX, decimal amountY) => new(this.X + amountX, this.Y + amountY); + public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor); - public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y); + /// + /// Stretches the point to the same proportional position in targetBounds as + /// it currently is in sourceBounds + /// + public PointInfo Stretch(RectangleInfo source, RectangleInfo target) => + new( + x: ((this.X - source.X) / source.Width * target.Width) + target.X, + y: ((this.Y - source.Y) / source.Height * target.Height) + target.Y); + + /// + /// Subtracts the specified point from the current point. + /// + public PointInfo Subtract(PointInfo point) => + new( + x: this.X - point.X, + y: this.Y - point.Y); public Point ToPoint() => new((int)this.X, (int)this.Y); @@ -51,17 +74,6 @@ public sealed class PointInfo return new((int)this.X, (int)this.Y); } - /// - /// Stretches the point to the same proportional position in targetBounds as - /// it currently is in sourceBounds - /// - public PointInfo Stretch(RectangleInfo source, RectangleInfo target) - { - return new PointInfo( - x: ((this.X - source.X) / source.Width * target.Width) + target.X, - y: ((this.Y - source.Y) / source.Height * target.Height) + target.Y); - } - public PointInfo Truncate() => new( (int)this.X, diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Drawing/RectangleInfo.cs similarity index 87% rename from src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs rename to src/modules/MouseUtils/MouseJump.Models/Drawing/RectangleInfo.cs index ab5bec8676..88e730e7ab 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Drawing/RectangleInfo.cs @@ -2,12 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Drawing; using System.Text.Json.Serialization; -using MouseJump.Common.Models.Styles; -using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle; +using MouseJump.Models.Styles; -namespace MouseJump.Common.Models.Drawing; +using BorderStyle = MouseJump.Models.Styles.BorderStyle; + +namespace MouseJump.Models.Drawing; /// /// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. @@ -16,6 +18,7 @@ public sealed class RectangleInfo { public static readonly RectangleInfo Empty = new(0, 0, 0, 0); + [JsonConstructor] public RectangleInfo(decimal x, decimal y, decimal width, decimal height) { this.X = x; @@ -29,7 +32,7 @@ public sealed class RectangleInfo { } - public RectangleInfo(Point location, SizeInfo size) + public RectangleInfo(PointInfo location, SizeInfo size) : this(location.X, location.Y, size.Width, size.Height) { } @@ -39,26 +42,34 @@ public sealed class RectangleInfo { } + [JsonPropertyName("x")] public decimal X { get; } + [JsonPropertyName("y")] public decimal Y { get; } + [JsonPropertyName("width")] public decimal Width { get; } + [JsonPropertyName("height")] public decimal Height { get; } + [JsonIgnore] + public bool IsEmpty => + object.ReferenceEquals(this, RectangleInfo.Empty); + [JsonIgnore] public decimal Left => this.X; @@ -83,6 +94,9 @@ public sealed class RectangleInfo public PointInfo Location => new(this.X, this.Y); + /// + /// Gets the midpoint of the current rectangle. + /// [JsonIgnore] public PointInfo Midpoint => new( @@ -104,27 +118,6 @@ public sealed class RectangleInfo width: this.Width, height: this.Height); - /// - /// Returns a new that is moved within the bounds of the specified outer rectangle. - /// If the current rectangle is larger than the outer rectangle, an exception is thrown. - /// - /// The outer within which to confine this rectangle. - /// A new that is the result of moving this rectangle within the bounds of the outer rectangle. - /// Thrown when the current rectangle is larger than the outer rectangle. - public RectangleInfo Clamp(RectangleInfo outer) - { - if ((this.Width > outer.Width) || (this.Height > outer.Height)) - { - throw new ArgumentException($"Value cannot be larger than {nameof(outer)}."); - } - - return new( - x: Math.Clamp(this.X, outer.X, outer.Right - this.Width), - y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height), - width: this.Width, - height: this.Height); - } - /// /// Adapted from https://github.com/dotnet/runtime /// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs @@ -186,6 +179,53 @@ public sealed class RectangleInfo this.Width + padding.Horizontal, this.Height + padding.Vertical); + public RectangleInfo MoveTo(PointInfo point) => + new( + point.X, + point.Y, + this.Width, + this.Height); + + /// + /// Adapted from https://github.com/dotnet/runtime + /// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs + /// + public RectangleInfo Intersect(RectangleInfo other) + { + var x1 = Math.Max(this.X, other.X); + var x2 = Math.Min(this.X + this.Width, other.X + other.Width); + var y1 = Math.Max(this.Y, other.Y); + var y2 = Math.Min(this.Y + this.Height, other.Y + other.Height); + + if (x2 >= x1 && y2 >= y1) + { + return new RectangleInfo(x1, y1, x2 - x1, y2 - y1); + } + + return RectangleInfo.Empty; + } + + /// + /// Returns a new that is moved within the bounds of the specified outer rectangle. + /// If the current rectangle is larger than the outer rectangle, an exception is thrown. + /// + /// The outer within which to confine this rectangle. + /// A new that is the result of moving this rectangle within the bounds of the outer rectangle. + /// Thrown when the current rectangle is larger than the outer rectangle. + public RectangleInfo MoveInside(RectangleInfo outer) + { + if ((this.Width > outer.Width) || (this.Height > outer.Height)) + { + throw new ArgumentException($"Value cannot be larger than {nameof(outer)}."); + } + + return new( + x: Math.Clamp(this.X, outer.X, outer.Right - this.Width), + y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height), + width: this.Width, + height: this.Height); + } + /// /// Returns a new that is offset by the specified amount. /// @@ -203,6 +243,9 @@ public sealed class RectangleInfo public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height); + public RectangleInfo Resize(decimal width, decimal height) => + new(this.X, this.Y, width, height); + public RectangleInfo Round() => this.Round(0); @@ -289,6 +332,16 @@ public sealed class RectangleInfo return new RectangleInfo(x1, y1, x2 - x1, y2 - y1); } + public static RectangleInfo Union(IEnumerable rects) + { + var list = rects.ToList(); + return (list.Count == 0) + ? RectangleInfo.Empty + : list.Skip(1).Aggregate( + seed: list.First(), + (aggr, rect) => aggr.Union(rect)); + } + public Rectangle ToRectangle() => new( (int)this.X, diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs b/src/modules/MouseUtils/MouseJump.Models/Drawing/SizeInfo.cs similarity index 86% rename from src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs rename to src/modules/MouseUtils/MouseJump.Models/Drawing/SizeInfo.cs index dc605ebb39..4534fc6cb8 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Drawing/SizeInfo.cs @@ -2,16 +2,23 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MouseJump.Common.Models.Styles; -using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle; +using System.Drawing; +using System.Text.Json.Serialization; -namespace MouseJump.Common.Models.Drawing; +using MouseJump.Models.Styles; + +using BorderStyle = MouseJump.Models.Styles.BorderStyle; + +namespace MouseJump.Models.Drawing; /// /// Immutable version of a System.Drawing.Size object with some extra utility methods. /// public sealed class SizeInfo { + public static readonly SizeInfo Empty = new(0, 0); + + [JsonConstructor] public SizeInfo(decimal width, decimal height) { this.Width = width; @@ -33,6 +40,9 @@ public sealed class SizeInfo get; } + public bool IsEmpty => + object.ReferenceEquals(this, SizeInfo.Empty); + public SizeInfo Clamp(SizeInfo max) { return new( @@ -40,13 +50,6 @@ public sealed class SizeInfo height: Math.Clamp(this.Height, 0, max.Height)); } - public SizeInfo Clamp(decimal maxWidth, decimal maxHeight) - { - return new( - width: Math.Clamp(this.Width, 0, maxWidth), - height: Math.Clamp(this.Height, 0, maxHeight)); - } - public SizeInfo Enlarge(BorderStyle border) => new( this.Width + border.Horizontal, @@ -101,7 +104,7 @@ public sealed class SizeInfo /// The x-coordinate of the upper-left corner of the rectangle. /// The y-coordinate of the upper-left corner of the rectangle. /// A new instance representing the positioned rectangle. - public RectangleInfo PlaceAt(decimal x, decimal y) => + public RectangleInfo MoveTo(decimal x, decimal y) => new(x, y, this.Width, this.Height); public SizeInfo Round() => @@ -120,21 +123,31 @@ public sealed class SizeInfo /// /// The size to fit this size into. /// A new instance representing the scaled size. - public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingRatio) + public SizeInfo ScaleToFit(SizeInfo bounds) + { + return this.ScaleToFit(bounds, out var _); + } + + /// + /// Scales this size to fit within the bounds of another size, while maintaining the aspect ratio. + /// + /// The size to fit this size into. + /// A new instance representing the scaled size. + public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingFactor) { var widthRatio = bounds.Width / this.Width; var heightRatio = bounds.Height / this.Height; switch (widthRatio.CompareTo(heightRatio)) { case < 0: - scalingRatio = widthRatio; + scalingFactor = widthRatio; return new(bounds.Width, this.Height * widthRatio); case 0: // widthRatio and heightRatio are the same, so just pick one - scalingRatio = widthRatio; + scalingFactor = widthRatio; return bounds; case > 0: - scalingRatio = heightRatio; + scalingFactor = heightRatio; return new(this.Width * heightRatio, bounds.Height); } } diff --git a/src/modules/MouseUtils/MouseJump.Models/MouseJump.Models.csproj b/src/modules/MouseUtils/MouseJump.Models/MouseJump.Models.csproj new file mode 100644 index 0000000000..37d9b18e57 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/MouseJump.Models.csproj @@ -0,0 +1,20 @@ + + + + + + {478C3576-CF1C-4509-B793-AE10DF394347} + PowerToys.MouseJump.Models + PowerToys.MouseJump.Models + PowerToys MouseJump.Models + Library + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps + false + false + + PerMonitorV2 + true + enable + enable + + diff --git a/src/modules/MouseUtils/MouseJump.Models/NintJsonConverter.cs b/src/modules/MouseUtils/MouseJump.Models/NintJsonConverter.cs new file mode 100644 index 0000000000..8060f9b7bb --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/NintJsonConverter.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 System.Text.Json; +using System.Text.Json.Serialization; + +namespace MouseJump.Models; + +public sealed class NintJsonConverter : JsonConverter +{ + public override nint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // nint can be 32 or 64 bit, so convert to Int64 + var value = reader.GetInt64(); + return (nint)value; + } + + public override void Write(Utf8JsonWriter writer, nint value, JsonSerializerOptions options) + { + writer.WriteNumberValue((long)value); + } +} diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Settings/PreviewType.cs b/src/modules/MouseUtils/MouseJump.Models/Settings/PreviewType.cs similarity index 85% rename from src/modules/MouseUtils/MouseJump.Common/Models/Settings/PreviewType.cs rename to src/modules/MouseUtils/MouseJump.Models/Settings/PreviewType.cs index 5a3e8076c7..b9df4dc37d 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Settings/PreviewType.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Settings/PreviewType.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Settings; +namespace MouseJump.Models.Settings; public enum PreviewType { diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/BackgroundStyle.cs similarity index 93% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/BackgroundStyle.cs index 1d6609a676..2008530189 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/BackgroundStyle.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Styles; +using System.Drawing; + +namespace MouseJump.Models.Styles; /// /// Represents the background fill style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/BorderStyle.cs similarity index 88% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/BorderStyle.cs index eb0ac423c5..53b2ba93ac 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/BorderStyle.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Styles; +using System.Drawing; + +namespace MouseJump.Models.Styles; /// /// Represents the border style for a drawing object. @@ -63,6 +65,11 @@ public sealed class BorderStyle public decimal Vertical => this.Top + this.Bottom; + public BorderStyle WithColor(Color? color) + { + return new BorderStyle(color, this.Left, this.Top, this.Right, this.Bottom, this.Depth); + } + public override string ToString() { return "{" + diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/BoxStyle.cs similarity index 82% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/BoxStyle.cs index 55e27c3b9f..1cb518bd44 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/BoxStyle.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Styles; +namespace MouseJump.Models.Styles; /// /// Represents the styles to apply to a simple box-layout based drawing object. @@ -29,18 +29,34 @@ public sealed class BoxStyle */ - public static readonly BoxStyle Empty = new(MarginStyle.Empty, BorderStyle.Empty, PaddingStyle.Empty, BackgroundStyle.Empty); + public static readonly BoxStyle Empty = new( + MarginStyle.Empty, + BorderStyle.Empty, + PaddingStyle.Empty, + BackgroundStyle.Empty, + isEmpty: true); public BoxStyle( MarginStyle marginStyle, BorderStyle borderStyle, PaddingStyle paddingStyle, BackgroundStyle backgroundStyle) + : this(marginStyle, borderStyle, paddingStyle, backgroundStyle, false) + { + } + + private BoxStyle( + MarginStyle marginStyle, + BorderStyle borderStyle, + PaddingStyle paddingStyle, + BackgroundStyle backgroundStyle, + bool isEmpty) { this.MarginStyle = marginStyle ?? throw new ArgumentNullException(nameof(marginStyle)); this.BorderStyle = borderStyle ?? throw new ArgumentNullException(nameof(borderStyle)); this.PaddingStyle = paddingStyle ?? throw new ArgumentNullException(nameof(paddingStyle)); this.BackgroundStyle = backgroundStyle ?? throw new ArgumentNullException(nameof(backgroundStyle)); + this.IsEmpty = isEmpty; } /// @@ -74,4 +90,9 @@ public sealed class BoxStyle { get; } + + public bool IsEmpty + { + get; + } } diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/MarginStyle.cs similarity index 96% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/MarginStyle.cs index b0a369dfe7..038502341b 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/MarginStyle.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Styles; +namespace MouseJump.Models.Styles; /// /// Represents the margin style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/PaddingStyle.cs similarity index 96% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/PaddingStyle.cs index d4b0c82b40..86373c3913 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/PaddingStyle.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.Models.Styles; +namespace MouseJump.Models.Styles; /// /// Represents the margin style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs b/src/modules/MouseUtils/MouseJump.Models/Styles/PreviewStyle.cs similarity index 66% rename from src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs rename to src/modules/MouseUtils/MouseJump.Models/Styles/PreviewStyle.cs index 2ca2b1bc28..ff1e2ad3e4 100644 --- a/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Models/Styles/PreviewStyle.cs @@ -2,20 +2,25 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MouseJump.Common.Models.Drawing; +using System.Collections.ObjectModel; +using System.Drawing; -namespace MouseJump.Common.Models.Styles; +using MouseJump.Models.Drawing; + +namespace MouseJump.Models.Styles; public sealed class PreviewStyle { public PreviewStyle( SizeInfo canvasSize, BoxStyle canvasStyle, - BoxStyle screenStyle) + BoxStyle screenStyle, + IEnumerable extraColors) { this.CanvasSize = canvasSize ?? throw new ArgumentNullException(nameof(canvasSize)); this.CanvasStyle = canvasStyle ?? throw new ArgumentNullException(nameof(canvasStyle)); this.ScreenStyle = screenStyle ?? throw new ArgumentNullException(nameof(screenStyle)); + this.ExtraColors = (extraColors ?? throw new ArgumentNullException(nameof(extraColors))).ToList().AsReadOnly(); } public SizeInfo CanvasSize @@ -32,4 +37,9 @@ public sealed class PreviewStyle { get; } + + public ReadOnlyCollection ExtraColors + { + get; + } } diff --git a/src/modules/MouseUtils/MouseJump.Models/ViewModel/CanvasViewModel.cs b/src/modules/MouseUtils/MouseJump.Models/ViewModel/CanvasViewModel.cs new file mode 100644 index 0000000000..52e05c4cc5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/ViewModel/CanvasViewModel.cs @@ -0,0 +1,84 @@ +// 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 System.Collections.ObjectModel; + +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; + +namespace MouseJump.Models.ViewModel; + +public sealed class CanvasViewModel +{ + public sealed class Builder + { + public Builder() + { + this.DeviceLayouts = new(); + this.CanvasStyle = BoxStyle.Empty; + } + + /// + /// Gets or sets the layout bounds for the canvas. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds? CanvasBounds + { + get; + set; + } + + public BoxStyle CanvasStyle + { + get; + set; + } + + public List? DeviceLayouts + { + get; + set; + } + + public CanvasViewModel Build() + { + return new CanvasViewModel( + canvasBounds: this.CanvasBounds ?? throw new InvalidOperationException($"{nameof(this.CanvasBounds)} must be initialized before calling {nameof(this.Build)}."), + canvasStyle: this.CanvasStyle ?? throw new InvalidOperationException($"{nameof(this.CanvasStyle)} must be initialized before calling {nameof(this.Build)}."), + deviceLayouts: (this.DeviceLayouts ?? throw new InvalidOperationException($"{nameof(this.DeviceLayouts)} must be initialized before calling {nameof(this.Build)}.")) + .Select(builder => builder.Build())); + } + } + + public CanvasViewModel( + BoxBounds canvasBounds, + BoxStyle canvasStyle, + IEnumerable deviceLayouts) + { + this.CanvasBounds = canvasBounds ?? throw new ArgumentNullException(nameof(canvasBounds)); + this.CanvasStyle = canvasStyle ?? throw new ArgumentNullException(nameof(canvasStyle)); + this.DeviceLayouts = new( + (deviceLayouts ?? throw new ArgumentNullException(nameof(deviceLayouts))) + .ToList()); + } + + /// + /// Gets the layout bounds for the canvas. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds CanvasBounds + { + get; + } + + public BoxStyle CanvasStyle + { + get; + } + + public ReadOnlyCollection DeviceLayouts + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/ViewModel/DeviceViewModel.cs b/src/modules/MouseUtils/MouseJump.Models/ViewModel/DeviceViewModel.cs new file mode 100644 index 0000000000..ee426f8a46 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/ViewModel/DeviceViewModel.cs @@ -0,0 +1,100 @@ +// 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 System.Collections.ObjectModel; + +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; + +namespace MouseJump.Models.ViewModel; + +public sealed class DeviceViewModel +{ + public sealed class Builder + { + public Builder() + { + this.DeviceBounds = BoxBounds.Empty; + this.DeviceStyle = BoxStyle.Empty; + this.ScreenLayouts = new(); + } + + public DeviceInfo? DeviceInfo + { + get; + set; + } + + /// + /// Gets or sets the layout bounds for the device. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds DeviceBounds + { + get; + set; + } + + public BoxStyle DeviceStyle + { + get; + set; + } + + public List? ScreenLayouts + { + get; + set; + } + + public DeviceViewModel Build() + { + return new DeviceViewModel( + deviceInfo: this.DeviceInfo ?? throw new InvalidOperationException($"{nameof(this.DeviceInfo)} must be initialized before calling {nameof(this.Build)}."), + deviceBounds: this.DeviceBounds ?? throw new InvalidOperationException($"{nameof(this.DeviceBounds)} must be initialized before calling {nameof(this.Build)}."), + deviceStyle: this.DeviceStyle ?? throw new InvalidOperationException($"{nameof(this.DeviceStyle)} must be initialized before calling {nameof(this.Build)}."), + screenLayouts: (this.ScreenLayouts ?? throw new InvalidOperationException($"{nameof(this.ScreenLayouts)} must be initialized before calling {nameof(this.Build)}.")) + .Select(builder => builder.Build())); + } + } + + public DeviceViewModel( + DeviceInfo deviceInfo, + BoxBounds deviceBounds, + BoxStyle deviceStyle, + IEnumerable screenLayouts) + { + this.DeviceInfo = deviceInfo ?? throw new ArgumentNullException(nameof(deviceInfo)); + this.DeviceBounds = deviceBounds ?? throw new ArgumentNullException(nameof(deviceBounds)); + this.DeviceStyle = deviceStyle ?? throw new ArgumentNullException(nameof(deviceStyle)); + this.ScreenLayouts = new( + (screenLayouts ?? throw new ArgumentNullException(nameof(screenLayouts))) + .ToList()); + } + + public DeviceInfo DeviceInfo + { + get; + } + + /// + /// Gets the layout bounds for the device. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds DeviceBounds + { + get; + } + + public BoxStyle DeviceStyle + { + get; + } + + public ReadOnlyCollection ScreenLayouts + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/ViewModel/FormViewModel.cs b/src/modules/MouseUtils/MouseJump.Models/ViewModel/FormViewModel.cs new file mode 100644 index 0000000000..22a9b864b7 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/ViewModel/FormViewModel.cs @@ -0,0 +1,62 @@ +// 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 MouseJump.Models.Drawing; + +namespace MouseJump.Models.ViewModel; + +/// +/// Defines the preview form size and location. +/// +public sealed class FormViewModel +{ + public sealed class Builder + { + /// + /// Gets or sets the coordinates of the preview form on the host computer's desktop. + /// Coordinates are relative to the actual host desktop. + /// + public RectangleInfo? FormBounds + { + get; + set; + } + + public CanvasViewModel.Builder? CanvasLayout + { + get; + set; + } + + public FormViewModel Build() + { + return new FormViewModel( + formBounds: this.FormBounds ?? throw new InvalidOperationException($"{nameof(this.FormBounds)} must be initialized before calling {nameof(this.Build)}."), + canvasLayout: (this.CanvasLayout ?? throw new InvalidOperationException($"{nameof(this.CanvasLayout)} must be initialized before calling {nameof(this.Build)}.")) + .Build()); + } + } + + public FormViewModel( + RectangleInfo formBounds, + CanvasViewModel canvasLayout) + { + this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); + this.CanvasLayout = canvasLayout ?? throw new ArgumentNullException(nameof(canvasLayout)); + } + + /// + /// Gets the coordinates of the preview form on the host computer's desktop. + /// Coordinates are relative to the actual host virtual screen / desktop that the preview image will be shown on. + /// + public RectangleInfo FormBounds + { + get; + } + + public CanvasViewModel CanvasLayout + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJump.Models/ViewModel/ScreenViewModel.cs b/src/modules/MouseUtils/MouseJump.Models/ViewModel/ScreenViewModel.cs new file mode 100644 index 0000000000..f697cb2126 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Models/ViewModel/ScreenViewModel.cs @@ -0,0 +1,85 @@ +// 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 MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Styles; + +namespace MouseJump.Models.ViewModel; + +public sealed class ScreenViewModel +{ + public sealed class Builder + { + public Builder() + { + this.ScreenBounds = BoxBounds.Empty; + this.ScreenStyle = BoxStyle.Empty; + } + + public ScreenInfo? ScreenInfo + { + get; + set; + } + + /// + /// Gets or sets the layout bounds for the screen. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds ScreenBounds + { + get; + set; + } + + public BoxStyle ScreenStyle + { + get; + set; + } + + public ScreenViewModel Build() + { + return new ScreenViewModel( + screenInfo: this.ScreenInfo ?? throw new InvalidOperationException($"{nameof(this.ScreenInfo)} must be initialized before calling {nameof(this.Build)}."), + screenBounds: this.ScreenBounds ?? throw new InvalidOperationException($"{nameof(this.ScreenBounds)} must be initialized before calling {nameof(this.Build)}."), + screenStyle: this.ScreenStyle ?? throw new InvalidOperationException($"{nameof(this.ScreenStyle)} must be initialized before calling {nameof(this.Build)}.")); + } + } + + public ScreenViewModel( + ScreenInfo screenInfo, + BoxBounds screenBounds, + BoxStyle screenStyle) + { + this.ScreenInfo = screenInfo ?? throw new ArgumentNullException(nameof(screenInfo)); + this.ScreenBounds = screenBounds ?? throw new ArgumentNullException(nameof(screenBounds)); + this.ScreenStyle = screenStyle ?? throw new ArgumentNullException(nameof(screenStyle)); + } + + public ScreenInfo ScreenInfo + { + get; + } + + /// + /// Gets the layout bounds for the screen. + /// Coordinates are relative to the origin on the containing Form. + /// + public BoxBounds ScreenBounds + { + get; + } + + public BoxStyle ScreenStyle + { + get; + } + + public override string ToString() + { + return $"bounds: {this.ScreenBounds}, style: {this.ScreenStyle}"; + } +} diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/MouseJumpEventLoop.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/MouseJumpEventLoop.cs new file mode 100644 index 0000000000..419fb86040 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/MouseJumpEventLoop.cs @@ -0,0 +1,150 @@ +// 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 ManagedCommon; + +using Microsoft.UI.Dispatching; + +namespace MouseJump.WinUI3.Helpers; + +internal static class MouseJumpEventLoop +{ + /// + /// Based on NativeEventWaiter.WaitForEventLoop. + /// + public static void RunEventHandler(string eventName, Action callback, DispatcherQueue dispatcherQueue, CancellationToken cancel) + { + Logger.LogDebug($"starting event handler for event '{eventName}'"); + var thread = new Thread(() => + { + using var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + var waitHandles = new WaitHandle[] { cancel.WaitHandle, eventHandle }; + while (true) + { + Logger.LogDebug($"[{eventName}] - entering event handler loop"); + if (WaitHandle.WaitAny(waitHandles) == 1) + { + try + { + Logger.LogDebug($"[{eventName}] - invoking callback"); + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + if (!dispatcherQueue.TryEnqueue(() => + { + try + { + callback(); + tcs.SetResult(); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + })) + { + tcs.SetException(new InvalidOperationException("DispatcherQueue is not accepting work items.")); + } + + tcs.Task.GetAwaiter().GetResult(); + } + catch (Exception ex) + { + // if something went wrong the application could be in an unstable state. + // the main MouseJump module will restart the background process the next + // time the hotkey is invoked, so just rethrow the exception here. + Logger.LogDebug($"[{eventName}] - error occurred"); + Logger.LogError(ex.ToString()); + throw; + } + } + else + { + Logger.LogDebug($"[{eventName}] - exiting event handler loop"); + return; + } + } + }); + + thread.Name = "MouseJumpEventLoopThread"; + thread.IsBackground = true; + + thread.Start(); + } + + /// + /// Based on NativeEventWaiter.WaitForEventLoop, + /// but takes an async callback. + /// + public static void RunAsyncEventHandler(string eventName, Func callback, DispatcherQueue dispatcherQueue, CancellationToken cancel) + { + Logger.LogDebug($"starting event handler for event '{eventName}'"); + var thread = new Thread(() => + { + using var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + var waitHandles = new WaitHandle[] { cancel.WaitHandle, eventHandle }; + while (true) + { + Logger.LogDebug($"[{eventName}] - entering event handler loop"); + if (WaitHandle.WaitAny(waitHandles) == 1) + { + try + { + Logger.LogDebug($"[{eventName}] - invoking callback"); + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + if (!dispatcherQueue.TryEnqueue(() => + { + try + { + _ = callback().ContinueWith( + t => + { + if (t.IsFaulted) + { + tcs.SetException(t.Exception!.InnerExceptions); + } + else if (t.IsCanceled) + { + tcs.SetCanceled(); + } + else + { + tcs.SetResult(); + } + }, + TaskScheduler.Default); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + })) + { + tcs.SetException(new InvalidOperationException("DispatcherQueue is not accepting work items.")); + } + + tcs.Task.GetAwaiter().GetResult(); // block until callback completes + } + catch (Exception ex) + { + // if something went wrong the application could be in an unstable state. + // the main MouseJump module will restart the background process the next + // time the hotkey is invoked, so just rethrow the exception here. + Logger.LogDebug($"[{eventName}] - error occurred"); + Logger.LogError(ex.ToString()); + throw; + } + } + else + { + Logger.LogDebug($"[{eventName}] - exiting event handler loop"); + return; + } + } + }); + + thread.Name = "MouseJumpEventLoopThread"; + thread.IsBackground = true; + + thread.Start(); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/SettingsHelper.cs similarity index 88% rename from src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs rename to src/modules/MouseUtils/MouseJump.WinUI3/Helpers/SettingsHelper.cs index 6e19043547..d6904b56a2 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/SettingsHelper.cs @@ -8,14 +8,16 @@ using System.IO.Abstractions; using System.Threading; using ManagedCommon; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; -using MouseJump.Common.Helpers; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Settings; -using MouseJump.Common.Models.Styles; -namespace MouseJumpUI.Helpers; +using MouseJump.Common.Helpers; +using MouseJump.Models.Drawing; +using MouseJump.Models.Settings; +using MouseJump.Models.Styles; + +namespace MouseJump.WinUI3.Helpers; internal sealed class SettingsHelper { @@ -24,7 +26,7 @@ internal sealed class SettingsHelper this.LockObject = new(); this.CurrentSettings = this.LoadSettings(); - // delay loading settings on change by some time to avoid file in use exception + // delay loading settings on change by some time to avoid "file in use" exception var throttledActionInvoker = new ThrottledActionInvoker(); this.FileSystemWatcher = Helper.GetFileWatcher( moduleName: MouseJumpSettings.ModuleName, @@ -119,7 +121,7 @@ internal sealed class SettingsHelper canvasStyle: new( marginStyle: new(0), borderStyle: new( - color: ConfigHelper.DeserializeFromConfigColorString( + color: ColorHelper.DeserializeFromConfigColorString( properties.BorderColor), all: properties.BorderThickness, depth: properties.Border3dDepth @@ -128,9 +130,9 @@ internal sealed class SettingsHelper all: properties.BorderPadding ), backgroundStyle: new( - color1: ConfigHelper.DeserializeFromConfigColorString( + color1: ColorHelper.DeserializeFromConfigColorString( properties.BackgroundColor1), - color2: ConfigHelper.DeserializeFromConfigColorString( + color2: ColorHelper.DeserializeFromConfigColorString( properties.BackgroundColor2) ) ), @@ -139,19 +141,20 @@ internal sealed class SettingsHelper all: properties.ScreenMargin ), borderStyle: new( - color: ConfigHelper.DeserializeFromConfigColorString( + color: ColorHelper.DeserializeFromConfigColorString( properties.BezelColor), all: properties.BezelThickness, depth: properties.Bezel3dDepth ), paddingStyle: new(0), backgroundStyle: new( - color1: ConfigHelper.DeserializeFromConfigColorString( + color1: ColorHelper.DeserializeFromConfigColorString( properties.ScreenColor1), - color2: ConfigHelper.DeserializeFromConfigColorString( + color2: ColorHelper.DeserializeFromConfigColorString( properties.ScreenColor2) ) - )), + ), + extraColors: []), _ => throw new InvalidOperationException( $"Unhandled {nameof(PreviewType)} '{previewType}'"), }; diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/ThrottledActionInvoker.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/ThrottledActionInvoker.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Helpers/ThrottledActionInvoker.cs rename to src/modules/MouseUtils/MouseJump.WinUI3/Helpers/ThrottledActionInvoker.cs index 5153a7dade..0567ec61a1 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/ThrottledActionInvoker.cs +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Helpers/ThrottledActionInvoker.cs @@ -6,7 +6,7 @@ using System; using System.Threading; using System.Windows.Threading; -namespace MouseJumpUI.Helpers; +namespace MouseJump.WinUI3.Helpers; internal sealed class ThrottledActionInvoker { diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/MouseJump.WinUI3.csproj b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJump.WinUI3.csproj new file mode 100644 index 0000000000..2040903f7a --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJump.WinUI3.csproj @@ -0,0 +1,74 @@ + + + + + + + WinExe + MouseJump.WinUI3 + PowerToys.MouseJump.WinUI3 + app.manifest + true + false + true + None + false + false + true + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps + + enable + enable + DISABLE_XAML_GENERATED_MAIN + + + + + + + + + + + + + + + + + + + + + + PowerToys.GPOWrapper + $(OutDir) + false + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml new file mode 100644 index 0000000000..b6941ad299 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml.cs b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml.cs new file mode 100644 index 0000000000..a4959b2181 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/App.xaml.cs @@ -0,0 +1,112 @@ +// 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 ManagedCommon; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; + +using MouseJump.Common.Helpers; +using MouseJump.WinUI3.Helpers; +using MouseJump.WinUI3.UI; + +using PowerToys.Interop; + +using Application = Microsoft.UI.Xaml.Application; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. +namespace MouseJump.WinUI3; + +/// +/// Provides application-specific behavior to supplement the default Application class. +/// +public partial class App : Application, IDisposable +{ + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly DispatcherQueue _dispatcherQueue; + private bool disposedValue; + + /// + /// Initializes a new instance of the class. + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + try + { + Logger.LogInfo("entering App.OnLaunched"); + + var etwTrace = new ETWTrace(); + + var settingsHelper = new SettingsHelper(); + var previewWindow = new PreviewWindow(settingsHelper); + + // start the handler that listens for the "show preview" event + Logger.LogInfo("starting 'show preview' event handler"); + MouseJumpEventLoop.RunAsyncEventHandler( + Constants.MouseJumpShowPreviewEvent(), + previewWindow.ShowPreviewAsync, + _dispatcherQueue, + _cancellationTokenSource.Token); + + // start the handler that listens for the "terminate app" event + Logger.LogInfo("starting 'terminate' event loop"); + MouseJumpEventLoop.RunEventHandler( + Constants.TerminateMouseJumpSharedEvent(), + this.TerminateApp, + _dispatcherQueue, + _cancellationTokenSource.Token); + + Logger.LogInfo("leaving App.OnLaunched"); + + etwTrace?.Dispose(); + } + catch (Exception ex) + { + Logger.LogError(ex.ToString()); + throw; + } + } + + internal void TerminateApp() + { + Logger.LogInfo("exiting Mouse Jump."); + _cancellationTokenSource.Cancel(); + _dispatcherQueue.TryEnqueue(this.Exit); + } + + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + { + return; + } + + if (disposing) + { + this._cancellationTokenSource.Cancel(); + } + + disposedValue = true; + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml new file mode 100644 index 0000000000..88d4099358 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml.cs b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml.cs new file mode 100644 index 0000000000..52e9825741 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/MouseJumpXAML/PreviewWindow.xaml.cs @@ -0,0 +1,537 @@ +// 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 System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using ManagedCommon; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Input; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media.Imaging; + +using MouseJump.Common.Helpers; +using MouseJump.Common.Imaging; +using MouseJump.Common.Interop; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.ViewModel; +using MouseJump.WinUI3.Helpers; + +using Windows.Graphics; +using Windows.System; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. +namespace MouseJump.WinUI3.UI; + +/// +/// An empty window that can be used on its own or navigated to within a Frame. +/// +internal sealed partial class PreviewWindow : Window +{ + internal PreviewWindow(SettingsHelper settingsHelper) + { + this.SettingsHelper = settingsHelper ?? throw new ArgumentNullException(nameof(settingsHelper)); + this.InitializeComponent(); + this.InitializeWindow(); + } + + private FormViewModel? FormLayout + { + get; + set; + } + + private SettingsHelper SettingsHelper + { + get; + } + + /// + /// Initializes some settings on the application window. + /// + private void InitializeWindow() + { + var appWindow = this.AppWindow; + var presenter = appWindow.Presenter as OverlappedPresenter; + if (presenter != null) + { + var hWnd = (HWND)WinRT.Interop.WindowNative.GetWindowHandle(this); + + // get the current window style + PInvoke.SetLastError(0); + var result = PInvoke.GetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE); + if (result == 0) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult(result: result, success: lastError == 0, lastError: lastError, memberName: nameof(PInvoke.GetWindowLong)); + } + + // set the window to be borderless, with no title bar, and hide all of the max / min / close buttons + var style = (WINDOW_STYLE)result; + style &= ~WINDOW_STYLE.WS_OVERLAPPEDWINDOW; + style |= WINDOW_STYLE.WS_POPUP; + PInvoke.SetLastError(0); + result = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, (int)style); + if (result == 0) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult(result: result, success: lastError == 0, lastError: lastError, memberName: nameof(PInvoke.SetWindowLong)); + } + + // get the current extended window style + PInvoke.SetLastError(0); + result = PInvoke.GetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE); + if (result == 0) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult(result: result, success: lastError == 0, lastError: lastError, memberName: nameof(PInvoke.GetWindowLong)); + } + + // set the window to be borderless, with no title bar, and hide all of the max / min / close buttons + var exStyle = (WINDOW_EX_STYLE)result; + exStyle |= WINDOW_EX_STYLE.WS_EX_TOOLWINDOW; // hide the taskbar icon + exStyle |= WINDOW_EX_STYLE.WS_EX_TOPMOST; // make topmost + PInvoke.SetLastError(0); + result = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)exStyle); + if (result == 0) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult(result: result, success: lastError == 0, lastError: lastError, memberName: nameof(PInvoke.SetWindowLong)); + } + } + + this.Activated += this.PreviewWindow_Activated; + this.StackPanel.PreviewKeyDown += this.PreviewWindow_PreviewKeyDown; + this.PreviewImage.PreviewKeyDown += this.PreviewWindow_PreviewKeyDown; + this.PreviewImage.PointerPressed += this.PreviewImage_PointerPressed; + } + + private void PreviewWindow_Activated(object sender, WindowActivatedEventArgs e) + { + switch (e.WindowActivationState) + { + case WindowActivationState.CodeActivated: + this.PreviewImage.Focus(FocusState.Programmatic); + break; + case WindowActivationState.Deactivated: + this.HideWindow(); + break; + case WindowActivationState.PointerActivated: + this.PreviewImage.Focus(FocusState.Pointer); + break; + default: + throw new InvalidOperationException(); + } + } + + private void PreviewWindow_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key == VirtualKey.Escape) + { + this.HideWindow(); + return; + } + + var screens = ScreenHelper.GetAllScreens().ToList(); + if (screens.Count == 0) + { + return; + } + + var currentLocation = MouseHelper.GetCursorPosition(); + var currentScreen = ScreenHelper.GetScreenFromPoint(screens, currentLocation); + var currentScreenIndex = screens.IndexOf(currentScreen); + var targetScreen = default(ScreenInfo?); + + switch (e.Key) + { + case >= VirtualKey.Number1 and <= VirtualKey.Number9: + { + // number keys 1-9 - move to the numbered screen + var screenNumber = e.Key - VirtualKey.Number0; + /* note - screen *numbers* are 1-based, screen *indexes* are 0-based */ + targetScreen = (screenNumber <= screens.Count) + ? targetScreen = screens[screenNumber - 1] + : null; + break; + } + + case >= VirtualKey.NumberPad1 and <= VirtualKey.NumberPad9: + { + // numpad keys 1-9 - move to the numbered screen + var screenNumber = e.Key - VirtualKey.NumberPad0; + /* note - screen *numbers* are 1-based, screen *indexes* are 0-based */ + targetScreen = (screenNumber <= screens.Count) + ? targetScreen = screens[screenNumber - 1] + : null; + break; + } + + case VirtualKey.P: + // "P" - move to the primary screen + targetScreen = screens.Single(screen => screen.Primary); + break; + case VirtualKey.Left: + // move to the previous screen, looping back to the end if needed + var prevIndex = (currentScreenIndex - 1 + screens.Count) % screens.Count; + targetScreen = screens[prevIndex]; + break; + case VirtualKey.Right: + // move to the next screen, looping round to the start if needed + var nextIndex = (currentScreenIndex + 1) % screens.Count; + targetScreen = screens[nextIndex]; + break; + case VirtualKey.Home: + // move to the first screen + targetScreen = screens.First(); + break; + case VirtualKey.End: + // move to the last screen + targetScreen = screens.Last(); + break; + } + + if (targetScreen is not null) + { + MouseHelper.SetCursorPosition(targetScreen.DisplayArea.Midpoint); + this.HideWindow(); + } + } + + private void PreviewImage_PointerPressed(object sender, PointerRoutedEventArgs e) + { + Logger.LogInfo(string.Join( + '\n', + "-----------", + nameof(PreviewWindow.PreviewImage_PointerPressed), + "-----------")); + + if (!e.Pointer.PointerDeviceType.Equals(PointerDeviceType.Mouse)) + { + // not a mouse click + return; + } + + var pointerPoint = e.GetCurrentPoint((UIElement)sender); + Logger.LogInfo(string.Join( + '\n', + "Reporting mouse event args", + $"\tleft button = {pointerPoint.Properties.IsLeftButtonPressed}", + $"\tright button = {pointerPoint.Properties.IsRightButtonPressed}", + $"\tlocation = {pointerPoint.Position}")); + + if (pointerPoint.Properties.IsLeftButtonPressed) + { + if (this.FormLayout is null) + { + // there's no layout data so we can't work out what screen was clicked + throw new InvalidOperationException(); + } + + // get the *scaled* pointer location + var pointerLocation = new PointInfo((decimal)pointerPoint.Position.X, (decimal)pointerPoint.Position.Y); + + // we need to apply the high-dpi scaling ratio for the current monitor to the pointer location + var highDpiScalingRatio = this.GetHighDpiScalingRatio(); + pointerLocation = pointerLocation.Scale(1 / (decimal)highDpiScalingRatio); + + // work out which screenshot was clicked + var clickedScreen = this.FormLayout.CanvasLayout.DeviceLayouts + .SelectMany(deviceLayout => deviceLayout.ScreenLayouts) + .FirstOrDefault( + screenLayout => screenLayout.ScreenBounds.OuterBounds.Contains(pointerLocation)); + if (clickedScreen is null) + { + return; + } + + // find the device the clicked screenshot belongs to + var clickedDevice = this.FormLayout.CanvasLayout.DeviceLayouts + .FirstOrDefault( + deviceLayout => deviceLayout.ScreenLayouts.Contains(clickedScreen)); + if (clickedDevice is null) + { + return; + } + + // scale up the click onto the physical screen - the aspect ratio of the screenshot + // might be distorted compared to the physical screen due to the borders around the + // screenshot, so we need to work out the target location on the physical screen first + var clickedDisplayArea = clickedScreen.ScreenInfo.DisplayArea; + var clickedLocation = pointerLocation + .Stretch( + source: clickedScreen.ScreenBounds.ContentBounds, + target: clickedDisplayArea) + .Clamp( + new( + x: clickedDisplayArea.X + 1, + y: clickedDisplayArea.Y + 1, + width: clickedDisplayArea.Width - 1, + height: clickedDisplayArea.Height - 1 + )) + .Truncate(); + + // move mouse pointer + Logger.LogInfo($"clicked location = {clickedLocation}"); + MouseHelper.SetCursorPosition(clickedLocation); + } + + this.HideWindow(); + } + + public async Task ShowPreviewAsync() + { + Logger.LogInfo(string.Join( + '\n', + "-----------", + nameof(PreviewWindow.ShowPreviewAsync), + "-----------")); + + // hide the form while we redraw it... + await this.HideWindowAsync() + .ConfigureAwait(false); + + var stopwatch = Stopwatch.StartNew(); + + // capture this first so we get an accurate mouse location + // (in case the user moves it a few pixels while the form is rendered) + var activatedLocation = MouseHelper.GetCursorPosition(); + + var appSettings = this.SettingsHelper.CurrentSettings ?? throw new InvalidOperationException(); + var displayInfo = DeviceHelper.GetDisplayInfo(); + + var activatedScreen = DeviceHelper.GetActivatedScreen(displayInfo.Devices[0], activatedLocation); + + var previewStyle = SettingsHelper.GetActivePreviewStyle(appSettings); + var formLayout = LayoutHelper.GetFormLayout( + previewStyle, + displayInfo, + activatedScreen: activatedScreen, + activatedLocation: activatedLocation); + + // remember the layout so we can map the mouse clicks back to + // the appropriate device and screen location + this.FormLayout = formLayout; + + await this.PositionWindowAsync(formLayout.FormBounds) + .ConfigureAwait(false); + + var imageCopyServices = displayInfo.Devices + .Select( + deviceInfo => (IImageRegionCopyService)new DesktopImageRegionCopyService()) + .ToList(); + + await DrawingHelper.RenderPreviewAsync( + this.FormLayout.CanvasLayout, + activatedScreen, + imageCopyServices, + this.OnPreviewImageCreatedAsync, + this.OnPreviewImageUpdatedAsync) + .ConfigureAwait(false); + + stopwatch.Stop(); + + PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpShowEvent()); + + await this.ShowWindowAsync() + .ConfigureAwait(false); + } + + private void ClearPreview() + { + if (this.PreviewImage.Source is null) + { + return; + } + + this.PreviewImage.Source = null; + + // force preview image memory to be released - otherwise + // all the disposed images can pile up without being GC'ed + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + /// + /// Calculates the high-dpi scaling ratio based on the current monitor's display settings. + /// + private double GetHighDpiScalingRatio() + { + var hWnd = (HWND)WinRT.Interop.WindowNative.GetWindowHandle(this); + var windowDpi = PInvoke.GetDpiForWindow(hWnd); + ResultHandler.ThrowIfZero(result: (int)windowDpi, getLastError: true, memberName: nameof(PInvoke.GetDpiForWindow)); + var scalingRatio = (double)PInvoke.USER_DEFAULT_SCREEN_DPI / windowDpi; + return scalingRatio; + } + + private async Task InvokeOnUiThreadAsync(Action action) + { + // this might be called from a task that we're awaiting + // so we need to make sure we use the UI thread + var tcs = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); + + if (!this.DispatcherQueue.TryEnqueue( + () => + { + try + { + action(); + tcs.SetResult(true); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + })) + { + tcs.SetException( + new InvalidOperationException("DispatcherQueue is not accepting work items.")); + } + + // wait for the task to complete + await tcs.Task.ConfigureAwait(false); + } + + private void HideWindow() + { + this.AppWindow.Hide(); + this.ClearPreview(); + } + + private async Task HideWindowAsync() + { + await this.InvokeOnUiThreadAsync( + () => + { + this.HideWindow(); + }).ConfigureAwait(false); + } + + private async Task ShowWindowAsync() + { + await this.InvokeOnUiThreadAsync( + () => + { + var presenter = this.AppWindow.Presenter as OverlappedPresenter + ?? throw new InvalidOperationException(); + if (!this.Visible) + { + // we seem to need to turn off topmost and then re-enable it again + // when we show the form - otherwise it doesn't always get shown topmost... + presenter.IsAlwaysOnTop = false; + presenter.IsAlwaysOnTop = true; + } + + var hWnd = (HWND)WinRT.Interop.WindowNative.GetWindowHandle(this); + + // use raw Win32 calls instead of WinUI3's AppWindow.Show()/Activate()/Focus() + // because WinUI3's activation sequence intermittently fails to transfer keyboard + // focus, which causes the Window.Activated "Deactivated" event to never fire + // when the user clicks away, leaving the preview window stuck open. + _ = PInvoke.ShowWindow(hWnd, SHOW_WINDOW_CMD.SW_SHOW); + + var setForegroundResult = PInvoke.SetForegroundWindow(hWnd); + ResultHandler.HandleResult( + result: (int)setForegroundResult, + success: (bool)setForegroundResult, + getLastError: false, + nameof(PInvoke.SetForegroundWindow)); + + PInvoke.SetLastError(0); + var setFocusResult = PInvoke.SetFocus(hWnd); + if (setFocusResult.IsNull) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult( + result: setFocusResult, + success: lastError == 0, + lastError: lastError, + nameof(PInvoke.SetFocus)); + } + + PInvoke.SetLastError(0); + var setActiveResult = PInvoke.SetActiveWindow(hWnd); + if (setActiveResult.IsNull) + { + var lastError = Marshal.GetLastPInvokeError(); + ResultHandler.HandleResult( + result: setActiveResult, + success: lastError == 0, + lastError: lastError, + nameof(PInvoke.SetActiveWindow)); + } + }).ConfigureAwait(false); + } + + /// + /// Resize and position the form. + /// + private async Task PositionWindowAsync(RectangleInfo bounds) + { + await this.InvokeOnUiThreadAsync( + () => + { + // note - do this with two calls to MoveAndResize rather than one as there appears to + // be an issue with dpi scaling even when using PerMonitorV2, where if the window is + // resized *and* moved in one call the resize uses the scaling of the *current* + // monitor before it's moved. + // + // If the move then happens to be to a different monitor, *and* the monitor has a + // different dpi scaling configured, the window size is then wrong for the dpi scaling + // of the new monitor. + // + // the workaround seems to be to call MoveAndResize twice - the first call might + // resize it incorrectly, but it moves the window to the correct monitor, and the + // second call then resizes it correctly. + // + // see https://github.com/mikeclayton/FancyMouse/issues/2 for more details + var windowBounds = new RectInt32((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height); + this.AppWindow.MoveAndResize(windowBounds); + this.AppWindow.MoveAndResize(windowBounds); + }).ConfigureAwait(false); + } + + private async Task OnPreviewImageCreatedAsync(Bitmap preview) + { + await this.InvokeOnUiThreadAsync( + () => + { + this.ClearPreview(); + + // we need to apply the high-dpi scaling ratio for the current monitor to the image control size + var highDpiScalingRatio = this.GetHighDpiScalingRatio(); + + this.PreviewImage.Width = preview.Width * highDpiScalingRatio; + this.PreviewImage.Height = preview.Height * highDpiScalingRatio; + }).ConfigureAwait(false); + } + + private async Task OnPreviewImageUpdatedAsync(Bitmap preview) + { + await this.InvokeOnUiThreadAsync( + () => + { + this.ClearPreview(); + + var bitmapImage = new BitmapImage(); + using (var stream = new MemoryStream()) + { + preview.Save(stream, ImageFormat.Png); + stream.Position = 0; + bitmapImage.SetSource(stream.AsRandomAccessStream()); + } + + this.PreviewImage.Source = bitmapImage; + }).ConfigureAwait(false); + } +} diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/NativeMethods.txt b/src/modules/MouseUtils/MouseJump.WinUI3/NativeMethods.txt new file mode 100644 index 0000000000..893bc6b591 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/NativeMethods.txt @@ -0,0 +1,14 @@ +WINDOW_EX_STYLE +WINDOW_STYLE +GetWindowLong +SetWindowLong + +USER_DEFAULT_SCREEN_DPI +GetDpiForWindow + +SetLastError + +ShowWindow +SetForegroundWindow +SetFocus +SetActiveWindow diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/Program.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Program.cs new file mode 100644 index 0000000000..faefa6d032 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Program.cs @@ -0,0 +1,91 @@ +// 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 System.Globalization; +using System.Reflection; + +using ManagedCommon; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Dispatching; +using Microsoft.Windows.AppLifecycle; +using MouseJump.Common.Helpers; +using PowerToys.GPOWrapper; + +namespace MouseJump.WinUI3; + +public static class Program +{ + private static App? _app; + + [STAThread] + public static void Main(string[] args) + { + Logger.InitializeLogger("\\MouseJump\\Logs"); + Logger.LogInfo("MouseJump process started"); + + using var etwTrace = new ETWTrace(); + + WinRT.ComWrappersSupport.InitializeComWrappers(); + + // check Group Policy allows this app to run + if (GPOWrapper.GetConfiguredMouseJumpEnabledValue() == GpoRuleConfigured.Disabled) + { + Logger.LogWarning( + "Tried to start with a GPO policy setting the utility to always be disabled. " + + "Please contact your systems administrator."); + return; + } + + // make sure we're in the right high dpi mode otherwise pixel positions and sizes for + // screen captures get distorted and various coordinates aren't calculated correctly. + Logger.LogInfo("checking high dpi mode"); + DpiModeHelper.EnsurePerMonitorV2Enabled(); + Logger.LogInfo("high dpi mode is ok"); + + // validate command line arguments - we're expecting + // a single argument containing the runner pid + if ((args.Length != 1) || !int.TryParse(args[0], out var runnerPid)) + { + var message = string.Join("\r\n", new[] + { + "Invalid command line arguments.", + "Expected usage is:", + string.Empty, + $"{Assembly.GetExecutingAssembly().GetName().Name} ", + }); + Logger.LogInfo(message); + throw new InvalidOperationException(message); + } + + Logger.LogDebug($"Runner PID = {runnerPid}", runnerPid.ToString(CultureInfo.InvariantCulture)); + + // quit the app when the parent process terminates + RunnerHelper.WaitForPowerToysRunner(runnerPid, () => + { + Logger.LogInfo("PowerToys Runner exited."); + _app?.TerminateApp(); + }); + Logger.LogInfo($"Mouse Jump started from the PowerToys Runner. Runner pid={runnerPid}"); + + // prevent multiple instances of the app from running + var instanceKey = AppInstance.FindOrRegisterForKey("MouseJump_Instance"); + if (instanceKey.IsCurrent) + { + Logger.LogWarning("starting application"); + Microsoft.UI.Xaml.Application.Start((p) => + { + var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread()); + SynchronizationContext.SetSynchronizationContext(context); + _app = new App(); + }); + Logger.LogWarning("application exited"); + } + else + { + Logger.LogWarning("another instance is running. exiting"); + } + + return; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpShowEvent.cs similarity index 54% rename from src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs rename to src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpShowEvent.cs index 0d721bbd1e..487b28e049 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpShowEvent.cs @@ -7,12 +7,11 @@ using System.Diagnostics.Tracing; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; -namespace MouseJumpUI.Telemetry +namespace MouseJump.WinUI3.Telemetry; + +[EventData] +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] +public sealed class MouseJumpShowEvent : EventBase, IEvent { - [EventData] - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] - public class MouseJumpShowEvent : EventBase, IEvent - { - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; } diff --git a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs b/src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpTeleportCursorEvent.cs similarity index 53% rename from src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs rename to src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpTeleportCursorEvent.cs index e392ff6d1d..42064c2b47 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs +++ b/src/modules/MouseUtils/MouseJump.WinUI3/Telemetry/MouseJumpTeleportCursorEvent.cs @@ -7,12 +7,11 @@ using System.Diagnostics.Tracing; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; -namespace MouseJumpUI.Telemetry +namespace MouseJump.WinUI3.Telemetry; + +[EventData] +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] +public sealed class MouseJumpTeleportCursorEvent : EventBase, IEvent { - [EventData] - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] - public class MouseJumpTeleportCursorEvent : EventBase, IEvent - { - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; } diff --git a/src/modules/MouseUtils/MouseJump.WinUI3/app.manifest b/src/modules/MouseUtils/MouseJump.WinUI3/app.manifest new file mode 100644 index 0000000000..c8e96c265e --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.WinUI3/app.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + true + PerMonitorV2 + + + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj b/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj index c74aac6c94..4d2bca18c7 100644 --- a/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj +++ b/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj @@ -12,13 +12,13 @@ DynamicLibrary true - + v143 Unicode DynamicLibrary false - + v143 true Unicode @@ -32,7 +32,7 @@ - $(RepoRoot)$(Platform)\$(Configuration)\ + $(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps\ PowerToys.MouseJump diff --git a/src/modules/MouseUtils/MouseJump/dllmain.cpp b/src/modules/MouseUtils/MouseJump/dllmain.cpp index 85894cea15..b22687175b 100644 --- a/src/modules/MouseUtils/MouseJump/dllmain.cpp +++ b/src/modules/MouseUtils/MouseJump/dllmain.cpp @@ -44,6 +44,29 @@ namespace const wchar_t JSON_KEY_SHIFT[] = L"shift"; const wchar_t JSON_KEY_CODE[] = L"code"; const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut"; + + struct FindWindowByPidData + { + DWORD pid; + HWND hwnd; + }; + + // a callback function that can be used with EnumWindows to find the first + // top-level window belonging to a given PID. stores the HWND in data->hwnd + // and returns FALSE to stop enumeration when found, or TRUE to continue to + // the next window. works for hidden windows as well as visible ones. + BOOL CALLBACK FindWindowByPidProc(HWND hwnd, LPARAM lParam) + { + auto* data = reinterpret_cast(lParam); + DWORD windowPid = 0; + GetWindowThreadProcessId(hwnd, &windowPid); + if (windowPid == data->pid) + { + data->hwnd = hwnd; + return FALSE; + } + return TRUE; + } } // Implement the PowerToy Module Interface and all the required methods. @@ -55,7 +78,7 @@ private: // Hotkey to invoke the module - HANDLE m_hProcess; + HANDLE m_hProcess = nullptr; // Time to wait for process to close after sending WM_CLOSE signal static const int MAX_WAIT_MILLISEC = 10000; @@ -118,7 +141,7 @@ private: SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; - sei.lpFile = L"PowerToys.MouseJumpUI.exe"; + sei.lpFile = L"WinUI3Apps\\PowerToys.MouseJump.WinUI3.exe"; sei.nShow = SW_SHOWNORMAL; sei.lpParameters = executable_args.data(); if (ShellExecuteExW(&sei)) @@ -237,7 +260,10 @@ public: { Logger::trace("MouseJump::enable()"); ResetEvent(m_hInvokeEvent); + + // pre-launch the ui exe so its window is ready before the first hotkey press launch_process(); + m_enabled = true; Trace::EnableJumpTool(true); } @@ -269,6 +295,34 @@ public: launch_process(); } + // find the winui3 window by pid. if we don't find one (e.g. the + // process is still starting up), drop this activation — the user + // can press the hotkey again once it's ready + FindWindowByPidData findData{ GetProcessId(m_hProcess), nullptr }; + EnumWindows(FindWindowByPidProc, reinterpret_cast(&findData)); + if (findData.hwnd == nullptr) + { + Logger::warn(L"WinUI3 window not ready yet - dropping activation. Press the hotkey again."); + return true; + } + + // on_hotkey() is called by the "runner" process in response to a + // hotkey activation. the hotkey activation means the runner process + // hook thread has the "last input" status, which in turn means we + // can call SetForegroundWindow on the ui exe window to make it the + // foreground window. this is more reliable than other options like + // AllowSetForegroundWindow (which the runner invalidates by calling + // SendInput straight after it calls action()). + BOOL sfwResult = SetForegroundWindow(findData.hwnd); + if (sfwResult) + { + Logger::trace(L"SetForegroundWindow on WinUI3 window succeeded with result {}", sfwResult); + } + else + { + Logger::warn(L"SetForegroundWindow on WinUI3 window failed with result {}. {}", sfwResult, get_last_error_or_default(GetLastError())); + } + SetEvent(m_hInvokeEvent); return true; } diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs deleted file mode 100644 index efc4310665..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs +++ /dev/null @@ -1,94 +0,0 @@ -// 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 System.Windows.Forms; - -namespace MouseJumpUI; - -internal sealed partial class MainForm -{ - - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); - panel1 = new Panel(); - Thumbnail = new PictureBox(); - panel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)Thumbnail).BeginInit(); - SuspendLayout(); - // - // panel1 - // - panel1.BackColor = System.Drawing.SystemColors.Highlight; - panel1.Controls.Add(Thumbnail); - panel1.Dock = DockStyle.Fill; - panel1.Location = new System.Drawing.Point(0, 0); - panel1.Name = "panel1"; - panel1.Size = new System.Drawing.Size(800, 450); - panel1.TabIndex = 1; - // - // Thumbnail - // - Thumbnail.BackColor = System.Drawing.SystemColors.Control; - Thumbnail.Dock = DockStyle.Fill; - Thumbnail.Location = new System.Drawing.Point(5, 5); - Thumbnail.Name = "Thumbnail"; - Thumbnail.Size = new System.Drawing.Size(800, 450); - Thumbnail.SizeMode = PictureBoxSizeMode.Normal; - Thumbnail.TabIndex = 1; - Thumbnail.TabStop = false; - Thumbnail.Click += Thumbnail_Click; - // - // MainForm - // - AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - AutoScaleMode = AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(800, 450); - Controls.Add(panel1); - FormBorderStyle = FormBorderStyle.None; - Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon"); - KeyPreview = true; - Name = "MainForm"; - ShowInTaskbar = false; - StartPosition = FormStartPosition.Manual; - Text = "MouseJump"; - TopMost = true; - Deactivate += MainForm_Deactivate; - Load += MainForm_Load; - KeyDown += MainForm_KeyDown; - panel1.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)Thumbnail).EndInit(); - ResumeLayout(false); - } - - #endregion - - private Panel panel1; - private PictureBox Thumbnail; - -} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs deleted file mode 100644 index 6017c427fb..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ /dev/null @@ -1,261 +0,0 @@ -// 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 System; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; - -using ManagedCommon; -using MouseJump.Common.Helpers; -using MouseJump.Common.Imaging; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Layout; -using MouseJumpUI.Helpers; - -namespace MouseJumpUI; - -internal sealed partial class MainForm : Form -{ - public MainForm(SettingsHelper settingsHelper) - { - this.InitializeComponent(); - this.SettingsHelper = settingsHelper ?? throw new ArgumentNullException(nameof(settingsHelper)); - } - - private PreviewLayout? PreviewLayout - { - get; - set; - } - - public SettingsHelper SettingsHelper - { - get; - } - - private void MainForm_Load(object sender, EventArgs e) - { - } - - private void MainForm_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Escape) - { - this.OnDeactivate(EventArgs.Empty); - return; - } - - var screens = ScreenHelper.GetAllScreens().ToList(); - if (screens.Count == 0) - { - return; - } - - var currentLocation = MouseHelper.GetCursorPosition(); - var currentScreen = ScreenHelper.GetScreenFromPoint(screens, currentLocation); - var currentScreenIndex = screens.IndexOf(currentScreen); - var targetScreen = default(ScreenInfo?); - - switch (e.KeyCode) - { - case >= Keys.D1 and <= Keys.D9: - { - // number keys 1-9 - move to the numbered screen - var screenNumber = e.KeyCode - Keys.D0; - /* note - screen *numbers* are 1-based, screen *indexes* are 0-based */ - targetScreen = (screenNumber <= screens.Count) - ? targetScreen = screens[screenNumber - 1] - : null; - break; - } - - case >= Keys.NumPad1 and <= Keys.NumPad9: - { - // numpad keys 1-9 - move to the numbered screen - var screenNumber = e.KeyCode - Keys.NumPad0; - /* note - screen *numbers* are 1-based, screen *indexes* are 0-based */ - targetScreen = (screenNumber <= screens.Count) - ? targetScreen = screens[screenNumber - 1] - : null; - break; - } - - case Keys.P: - // "P" - move to the primary screen - targetScreen = screens.Single(screen => screen.Primary); - break; - case Keys.Left: - // move to the previous screen, looping back to the end if needed - var prevIndex = (currentScreenIndex - 1 + screens.Count) % screens.Count; - targetScreen = screens[prevIndex]; - break; - case Keys.Right: - // move to the next screen, looping round to the start if needed - var nextIndex = (currentScreenIndex + 1) % screens.Count; - targetScreen = screens[nextIndex]; - break; - case Keys.Home: - // move to the first screen - targetScreen = screens.First(); - break; - case Keys.End: - // move to the last screen - targetScreen = screens.Last(); - break; - } - - if (targetScreen is not null) - { - MouseHelper.SetCursorPosition(targetScreen.DisplayArea.Midpoint); - this.OnDeactivate(EventArgs.Empty); - } - } - - private void MainForm_Deactivate(object sender, EventArgs e) - { - this.Hide(); - this.ClearPreview(); - } - - private void Thumbnail_Click(object sender, EventArgs e) - { - var mouseEventArgs = (MouseEventArgs)e; - Logger.LogInfo(string.Join( - '\n', - $"Reporting mouse event args", - $"\tbutton = {mouseEventArgs.Button}", - $"\tlocation = {mouseEventArgs.Location}")); - - if (mouseEventArgs.Button == MouseButtons.Left) - { - if (this.PreviewLayout is null) - { - // there's no layout data so we can't work out what screen was clicked - return; - } - - // work out which screenshot was clicked - var clickedScreenshot = this.PreviewLayout.ScreenshotBounds - .FirstOrDefault( - box => box.BorderBounds.Contains(mouseEventArgs.X, mouseEventArgs.Y)); - if (clickedScreenshot is null) - { - return; - } - - // scale up the click onto the physical screen - the aspect ratio of the screenshot - // might be distorted compared to the physical screen due to the borders around the - // screenshot, so we need to work out the target location on the physical screen first - var clickedScreen = - this.PreviewLayout.Screens[this.PreviewLayout.ScreenshotBounds.IndexOf(clickedScreenshot)]; - var clickedLocation = new PointInfo(mouseEventArgs.Location) - .Stretch( - source: clickedScreenshot.ContentBounds, - target: clickedScreen) - .Clamp( - new( - x: clickedScreen.X + 1, - y: clickedScreen.Y + 1, - width: clickedScreen.Width - 1, - height: clickedScreen.Height - 1 - )) - .Truncate(); - - // move mouse pointer - Logger.LogInfo($"clicked location = {clickedLocation}"); - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); - MouseHelper.SetCursorPosition(clickedLocation); - } - - this.OnDeactivate(EventArgs.Empty); - } - - public void ShowPreview() - { - // hide the form while we redraw it... - this.Visible = false; - - var stopwatch = Stopwatch.StartNew(); - - var appSettings = this.SettingsHelper.CurrentSettings ?? throw new InvalidOperationException(); - var screens = ScreenHelper.GetAllScreens().Select(screen => screen.DisplayArea).ToList(); - var activatedLocation = MouseHelper.GetCursorPosition(); - - this.PreviewLayout = LayoutHelper.GetPreviewLayout( - previewStyle: SettingsHelper.GetActivePreviewStyle(appSettings), - screens: screens, - activatedLocation: activatedLocation); - - this.PositionForm(this.PreviewLayout.FormBounds); - - var imageCopyService = new DesktopImageRegionCopyService(); - DrawingHelper.RenderPreview( - this.PreviewLayout, - imageCopyService, - this.OnPreviewImageCreated, - this.OnPreviewImageUpdated); - - stopwatch.Stop(); - - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpShowEvent()); - - // we have to activate the form to make sure the deactivate event fires - this.Activate(); - } - - private void ClearPreview() - { - if (this.Thumbnail.Image is null) - { - return; - } - - var tmp = this.Thumbnail.Image; - this.Thumbnail.Image = null; - tmp.Dispose(); - - // force preview image memory to be released; otherwise, - // all the disposed images can pile up without being GC'ed - GC.Collect(); - } - - /// - /// Resize and position the specified form. - /// - private void PositionForm(RectangleInfo bounds) - { - // note - do this in two steps rather than "this.Bounds = formBounds" as there - // appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2, - // where the form scaling uses either the *primary* screen scaling or the *previous* - // screen's scaling when the form is moved to a different screen. i've got no idea - // *why*, but the exact sequence of calls below seems to be a workaround... - // see https://github.com/mikeclayton/FancyMouse/issues/2 - var rect = bounds.ToRectangle(); - this.Location = rect.Location; - _ = this.PointToScreen(Point.Empty); - this.Size = rect.Size; - } - - private void OnPreviewImageCreated(Bitmap preview) - { - this.ClearPreview(); - this.Thumbnail.Image = preview; - } - - private void OnPreviewImageUpdated() - { - if (!this.Visible) - { - // we seem to need to turn off topmost and then re-enable it again - // when we show the form; otherwise, it doesn't always get shown topmost... - this.TopMost = false; - this.TopMost = true; - this.Show(); - } - - this.Thumbnail.Refresh(); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.resx b/src/modules/MouseUtils/MouseJumpUI/MainForm.resx deleted file mode 100644 index 4df95b5495..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.resx +++ /dev/null @@ -1,377 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA - AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP9kdJz/ZHSc/2R0nP9kdJz/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAA/2R0nP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/wAAAP8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/2R0nP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/wAA - AP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP9kdJz/ZHSc/2R0 - nP9kdJz/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/8bi - 4/8AAAD/AAAA/wAAAP8AAAD/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAA/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/5Ok - pf8kISD/k6Sl/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAA - AP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAA - AP8AAAD/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi - 4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi - 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CY - tf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CY - tf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY - tf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/4CY - tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CY - tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/AAAA/wAAAP/G4uP/xuLj/8bi4//G4uP/AAAA/wAA - AP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi - 4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAD/AAAA/wAA - AP8AAAD/AAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAA - AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CY - tf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAA - AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY - tf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAA - AP/G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AP/G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAA - AP+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/xuLj/8bi - 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi - 4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///////wAA////////AAD///////8AAP///////wAA////////AAD///////8AAP// - /////wAA////////AAD///////8AAP///////wAA///8P///AAD///gf//8AAP//8A///wAA///gB/// - AAD//8AD//8AAP//gAH//wAA//8AAP//AAD//gAAf/8AAP/8AAA//wAA//wAAD//AAD/+AAAH/8AAP/4 - AAAf/wAA//AAAA//AAD/4AAAB/8AAP/AAAAD/wAA/8AAAAP/AAD/gAAAAf8AAP8AAAAA/wAA/wAAAAD/ - AAD/AAQgAP8AAP8AD/AA/wAA/wAf+AD/AAD/AD/8AP8AAP+AP/wB/wAA/8B//gP/AAD/wH/+A/8AAP/g - //8H/wAA//H//4//AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// - AAD///////8AAP///////wAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2R0nP9kdJz/ZHSc/2R0nP8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/2R0 - nP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/2R0nP9kdJz/ZHSc/2R0 - nP9kdJz/ZHSc/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/2R0 - nP9kdJz/ZHSc/2R0nP8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi - 4//G4uP/AAAA/wAAAP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AP/G4uP/xuLj/8bi4/+TpKX/JCEg/5Okpf/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/+TpKX/JCEg/5Ok - pf/G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAA/8bi4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/yQh - IP8kISD/JCEg/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4//G4uP/k6Sl/yQhIP+TpKX/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/k6Sl/yQhIP+TpKX/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAD/AAAA/8bi4//G4uP/AAAA/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi - 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi - 4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY - tf+AmLX/AAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY - tf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/gJi1/4CY - tf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CY - tf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY - tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAA - AP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAP/G4uP/xuLj/wAA - AP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi - 4//G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAA/8bi - 4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/AAAA/wAAAP/G4uP/xuLj/8bi - 4//G4uP/AAAA/wAAAP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAA - AP8AAAD/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA - AP8AAAD/AAAA/wAAAP8AAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAA - AP/G4uP/AAAA/wAAAP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY - tf+AmLX/AAAA/8bi4/8AAAD/AAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAA - AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY - tf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi - 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi - 4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/wAA - AP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAD/xuLj/8bi4/8AAAD/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAD/xuLj/wAA - AP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA - AP/G4uP/xuLj/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4/8AAAD/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - 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 - rEEAAKxBA8CsQQfgrEGH4axBj/GsQf//rEE= - - - \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj deleted file mode 100644 index 53f949f4c3..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - PowerToys.MouseJumpUI - PowerToys.MouseJumpUI - PowerToys MouseJumpUI - WinExe - $(RepoRoot)$(Platform)\$(Configuration) - false - false - - PerMonitorV2 - true - enable - true - disable - - - - - - PowerToys.GPOWrapper - $(OutDir) - false - - - - - - - - - - - - \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/Program.cs b/src/modules/MouseUtils/MouseJumpUI/Program.cs deleted file mode 100644 index d327e80304..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/Program.cs +++ /dev/null @@ -1,98 +0,0 @@ -// 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 System; -using System.Reflection; -using System.Threading; -using System.Windows.Forms; -using System.Windows.Threading; - -using Common.UI; -using ManagedCommon; -using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Telemetry; -using MouseJumpUI.Helpers; -using PowerToys.Interop; - -namespace MouseJumpUI; - -internal static class Program -{ - private static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - /// - /// The main entry point for the application. - /// - [STAThread] - private static void Main(string[] args) - { - Logger.InitializeLogger("\\MouseJump\\Logs"); - ETWTrace etwTrace = new ETWTrace(); - - // To customize application configuration such as set high DPI settings or default font, - // see https://aka.ms/applicationconfiguration. - ApplicationConfiguration.Initialize(); - - if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseJumpEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) - { - // TODO : Log message - Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); - return; - } - - if (Application.HighDpiMode != HighDpiMode.PerMonitorV2) - { - Logger.LogError("High dpi mode is not set to PerMonitorV2."); - return; - } - - // validate command line arguments - we're expecting - // a single argument containing the runner pid - if ((args.Length != 1) || !int.TryParse(args[0], out var runnerPid)) - { - var message = string.Join("\r\n", new[] - { - "Invalid command line arguments.", - "Expected usage is:", - string.Empty, - $"{Assembly.GetExecutingAssembly().GetName().Name} ", - }); - Logger.LogInfo(message); - throw new InvalidOperationException(message); - } - - Logger.LogInfo($"Mouse Jump started from the PowerToys Runner. Runner pid={runnerPid}"); - - RunnerHelper.WaitForPowerToysRunner(runnerPid, () => - { - Logger.LogInfo("PowerToys Runner exited."); - TerminateApp(); - }); - - var settingsHelper = new SettingsHelper(); - var mainForm = new MainForm(settingsHelper); - - NativeEventWaiter.WaitForEventLoop( - Constants.MouseJumpShowPreviewEvent(), - mainForm.ShowPreview, - Dispatcher.CurrentDispatcher, - cancellationTokenSource.Token); - - NativeEventWaiter.WaitForEventLoop( - Constants.TerminateMouseJumpSharedEvent(), - TerminateApp, - Dispatcher.CurrentDispatcher, - cancellationTokenSource.Token); - - Application.Run(); - etwTrace?.Dispose(); - } - - private static void TerminateApp() - { - Logger.LogInfo("Exiting Mouse Jump."); - cancellationTokenSource.Cancel(); - Application.Exit(); - } -} diff --git a/src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj b/src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj index 33d722718f..dd9612d203 100644 --- a/src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj +++ b/src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj @@ -1,25 +1,26 @@ - - + + - - {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} - PowerToys.MouseUtils.UITests - PowerToys.MouseUtils.UITests - false - true - enable - Library - - - false - - $(SolutionDir)$(Platform)\$(Configuration)\tests\MouseUtils.UITests\ + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} + PowerToys.MouseUtils.UITests + PowerToys.MouseUtils.UITests + false + true + enable + Library + + + false + + + + $(SolutionDir)$(Platform)\$(Configuration)\tests\MouseUtils.UITests\ - - + + diff --git a/src/runner/main.cpp b/src/runner/main.cpp index dd7a480835..7dd7c403ee 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -265,7 +265,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"PowerToys.AwakeModuleInterface.dll", L"PowerToys.FindMyMouse.dll", L"PowerToys.MouseHighlighter.dll", - L"PowerToys.MouseJump.dll", + L"WinUI3Apps/PowerToys.MouseJump.dll", L"PowerToys.AlwaysOnTopModuleInterface.dll", L"PowerToys.MousePointerCrosshairs.dll", L"PowerToys.CursorWrap.dll", diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs index 91944368a1..e18b00c426 100644 --- a/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs @@ -6,11 +6,13 @@ using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + using MouseJump.Common.Helpers; -using MouseJump.Common.Models.Settings; +using MouseJump.Models.Settings; namespace Microsoft.PowerToys.Settings.UI.Library { @@ -108,24 +110,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library // set default values for custom preview style var previewStyle = StyleHelper.BezelledPreviewStyle; this.Properties.PreviewType = PreviewType.Bezelled.ToString(); - this.Properties.BackgroundColor1 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color1)); - this.Properties.BackgroundColor2 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color2)); + this.Properties.BackgroundColor1 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color1)); + this.Properties.BackgroundColor2 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color2)); this.Properties.BorderThickness = (int)previewStyle.CanvasStyle.BorderStyle.Top; - this.Properties.BorderColor = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BorderStyle.Color)); + this.Properties.BorderColor = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.CanvasStyle.BorderStyle.Color)); this.Properties.Border3dDepth = (int)previewStyle.CanvasStyle.BorderStyle.Depth; this.Properties.BorderPadding = (int)previewStyle.CanvasStyle.PaddingStyle.Top; this.Properties.BezelThickness = (int)previewStyle.ScreenStyle.BorderStyle.Top; - this.Properties.BezelColor = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BorderStyle.Color)); + this.Properties.BezelColor = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.ScreenStyle.BorderStyle.Color)); this.Properties.Bezel3dDepth = (int)previewStyle.ScreenStyle.BorderStyle.Depth; this.Properties.ScreenMargin = (int)previewStyle.ScreenStyle.MarginStyle.Top; - this.Properties.ScreenColor1 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color1)); - this.Properties.ScreenColor2 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color2)); + this.Properties.ScreenColor1 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color1)); + this.Properties.ScreenColor2 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color2)); } // we still need to flag the settings as "upgraded" so that the new version gets written diff --git a/src/settings-ui/Settings.UI/Converters/MouseJumpPreviewTypeConverter.cs b/src/settings-ui/Settings.UI/Converters/MouseJumpPreviewTypeConverter.cs index cfc980a8ce..fd4c96d414 100644 --- a/src/settings-ui/Settings.UI/Converters/MouseJumpPreviewTypeConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/MouseJumpPreviewTypeConverter.cs @@ -4,7 +4,7 @@ using System; using Microsoft.UI.Xaml.Data; -using MouseJump.Common.Models.Settings; +using MouseJump.Models.Settings; namespace Microsoft.PowerToys.Settings.UI.Converters { diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 381e7968e1..72e365c5b9 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -119,12 +119,13 @@ + + - diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml.cs index de5eabb844..101efdaa25 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml.cs @@ -11,7 +11,7 @@ using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using MouseJump.Common.Helpers; -using MouseJump.Common.Models.Settings; +using MouseJump.Models.Settings; namespace Microsoft.PowerToys.Settings.UI.Panels { @@ -128,24 +128,24 @@ namespace Microsoft.PowerToys.Settings.UI.Panels // note that we have to replace Named and System colors with their ARGB equivalents // so that serialization returns an ARGB string rather than the Named or System color *name*. this.ViewModel.MouseJumpPreviewType = selectedPreviewType.ToString(); - this.ViewModel.MouseJumpBackgroundColor1 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color1)); - this.ViewModel.MouseJumpBackgroundColor2 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color2)); + this.ViewModel.MouseJumpBackgroundColor1 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color1)); + this.ViewModel.MouseJumpBackgroundColor2 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color2)); this.ViewModel.MouseJumpBorderThickness = (int)selectedPreviewStyle.CanvasStyle.BorderStyle.Top; - this.ViewModel.MouseJumpBorderColor = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BorderStyle.Color)); + this.ViewModel.MouseJumpBorderColor = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BorderStyle.Color)); this.ViewModel.MouseJumpBorder3dDepth = (int)selectedPreviewStyle.CanvasStyle.BorderStyle.Depth; this.ViewModel.MouseJumpBorderPadding = (int)selectedPreviewStyle.CanvasStyle.PaddingStyle.Top; this.ViewModel.MouseJumpBezelThickness = (int)selectedPreviewStyle.ScreenStyle.BorderStyle.Top; - this.ViewModel.MouseJumpBezelColor = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BorderStyle.Color)); + this.ViewModel.MouseJumpBezelColor = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BorderStyle.Color)); this.ViewModel.MouseJumpBezel3dDepth = (int)selectedPreviewStyle.ScreenStyle.BorderStyle.Depth; this.ViewModel.MouseJumpScreenMargin = (int)selectedPreviewStyle.ScreenStyle.MarginStyle.Top; - this.ViewModel.MouseJumpScreenColor1 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color1)); - this.ViewModel.MouseJumpScreenColor2 = ConfigHelper.SerializeToConfigColorString( - ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color2)); + this.ViewModel.MouseJumpScreenColor1 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color1)); + this.ViewModel.MouseJumpScreenColor2 = ColorHelper.SerializeToConfigColorString( + ColorHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color2)); } private PreviewType GetSelectedPreviewType() diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs index e954976f2e..106e91b131 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs @@ -11,17 +11,21 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; + using MouseJump.Common.Helpers; using MouseJump.Common.Imaging; -using MouseJump.Common.Models.Drawing; -using MouseJump.Common.Models.Settings; -using MouseJump.Common.Models.Styles; +using MouseJump.Models.Display; +using MouseJump.Models.Drawing; +using MouseJump.Models.Settings; +using MouseJump.Models.Styles; namespace Microsoft.PowerToys.Settings.UI.ViewModels { @@ -154,19 +158,38 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { get { - // keep these in sync with the layout of "Images\MouseJump-Desktop.png" - var screens = new List() - { - /* - these magic numbers are the pixel dimensions of the individual screens on the - fake desktop image - "Images\MouseJump-Desktop.png" - used to generate the - preview image in the Settings UI properties page for Mouse Jump. if you update - the fake desktop image be sure to update these values as well. - */ - new(635, 172, 272, 168), - new(0, 0, 635, 339), - }; - var desktopSize = LayoutHelper.GetCombinedScreenBounds(screens).Size; + // build the display info used to generate the preview image in the settings dialog + // (keep the values in sync with the layout of "Images\MouseJump-Desktop.png") + var displayInfo = new DisplayInfo([ + new DeviceInfo( + hostname: "FakeDisplay1", + localhost: true, + screens: [ + /* + these magic numbers are the pixel dimensions of the individual screens on the + fake desktop image - "Images\MouseJump-Desktop.png" - used to generate the + preview image in the Settings UI properties page for Mouse Jump. if you update + the fake desktop image be sure to update these values as well. + */ + new( + handle: IntPtr.Zero, + primary: false, + displayArea: new RectangleInfo(635, 172, 272, 168), + workingArea: new RectangleInfo(635, 172, 272, 168)), + new( + handle: IntPtr.Zero, + primary: true, + displayArea: new RectangleInfo(0, 0, 635, 339), + workingArea: new RectangleInfo(0, 0, 635, 339)), + ]), + ]); + + var desktopSize = LayoutHelper.GetCombinedScreenBounds( + displayInfo.Devices[0].Screens + .Select(screen => screen.DisplayArea) + .ToList()) + .Size; + /* magic number 283 is the content height left in the settings card after removing the top and bottom chrome: @@ -174,7 +197,15 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels this ensures we get a preview image scaled at 100% so borders, etc., are shown at exact pixel sizes in the preview */ - var canvasSize = new SizeInfo(desktopSize.Width, 283).Clamp(desktopSize); + const int settingsCardHeight = 300; + const int settingsCardTopBorder = 1; + const int settingsCardTopMargin = 7; + const int settingsCardBottomMargin = 8; + const int settingsCardBottomBorder = 1; + const int settingsCardContentHeight = // 283 + settingsCardHeight - settingsCardTopBorder - settingsCardTopMargin - settingsCardBottomBorder - settingsCardBottomMargin; + + var canvasSize = new SizeInfo(desktopSize.Width, settingsCardContentHeight).Clamp(desktopSize); var previewType = Enum.TryParse(this.MouseJumpPreviewType, true, out var previewTypeResult) ? previewTypeResult @@ -188,7 +219,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels canvasStyle: new( marginStyle: new(0), borderStyle: new( - color: ConfigHelper.DeserializeFromConfigColorString( + color: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpBorderColor), all: this.MouseJumpBorderThickness, depth: this.MouseJumpBorder3dDepth @@ -197,9 +228,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels all: this.MouseJumpBorderPadding ), backgroundStyle: new( - color1: ConfigHelper.DeserializeFromConfigColorString( + color1: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpBackgroundColor1), - color2: ConfigHelper.DeserializeFromConfigColorString( + color2: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpBackgroundColor2) ) ), @@ -208,33 +239,39 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels all: this.MouseJumpScreenMargin ), borderStyle: new( - color: ConfigHelper.DeserializeFromConfigColorString( + color: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpBezelColor), all: this.MouseJumpBezelThickness, depth: this.MouseJumpBezel3dDepth ), paddingStyle: new(0), backgroundStyle: new( - color1: ConfigHelper.DeserializeFromConfigColorString( + color1: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpScreenColor1), - color2: ConfigHelper.DeserializeFromConfigColorString( + color2: ColorHelper.DeserializeFromConfigColorString( this.MouseJumpScreenColor2) ) - )), + ), + extraColors: []), _ => throw new InvalidOperationException( $"Unhandled {nameof(MouseJumpPreviewType)} '{previewType}'"), }; - var previewLayout = LayoutHelper.GetPreviewLayout( + var activatedScreen = displayInfo.Devices[0].Screens[0]; + + var previewLayout = LayoutHelper.GetFormLayout( previewStyle: previewStyle, - screens: screens, + displayInfo: displayInfo, + activatedScreen: activatedScreen, activatedLocation: new(0, 0)); var desktopImage = MouseUtilsViewModel.MouseJumpDesktopImage.Value; var imageCopyService = new StaticImageRegionCopyService(desktopImage); - using var previewImage = DrawingHelper.RenderPreview( - previewLayout, - imageCopyService); + var previewImageTask = DrawingHelper.RenderPreviewAsync( + previewLayout.CanvasLayout, + activatedScreen, + new() { imageCopyService }); + using var previewImage = previewImageTask.GetAwaiter().GetResult(); // save the image to a memory stream using var stream = new MemoryStream(); diff --git a/tools/MouseJump.Kicker/Assets/Icons/MouseJump 16x16.ico b/tools/MouseJump.Kicker/Assets/Icons/MouseJump 16x16.ico new file mode 100644 index 0000000000000000000000000000000000000000..8b189f4e112db98d2773bf9bd7cb3906940e5b4f GIT binary patch literal 1150 zcmd6mNl#Nz7>19LKq;j(7>Ns11{r>WQ8)4fTphcR1sfO0APN*26i5IY18O3IqJ%*x zAd?bJ9GPmF%2aHL2q;4@9iVA29gsm^zm}j2-MG?o^W2ksx!?1>=OmZ0ee}p=jNV}^ z(TA}R##k&tC93S!Y5OnNP*y}+4ke%_NT;@wHU{OyH5 zBBy$sHdlg?>;Md33BX_mgQoNx232KvlgOaHBtqXs5qeYo(39+k?j#SspuV5z3ZLSVMq~(febO;rTasD*&qFBp%OgCl2;c~%dH9>PT9wS){qq)*w85Lp-WQw8jILSA6D;Tq7!T4Bu0Qy1(ohkf{A=7ma7h7sgmJCa~iB>0nUw0xHi6e=Wp$N z!}fPz{^cFaDCwIUG=qAFl*cjVbp}DG3B~dQ2H`~>Y%{%B(?5qz{|q)=2kcXwuuryO zr7IJkZ`0kBpMZIG0?scs%&S|WFJUk>gkj|oQ74A6n!)&xAm4Ho!_qyXVs~FGrx~x5 z*jt-U8|JD`!uRI6 z`#xChjDfWy8rJqGSlS5kSz0{K$lshotq7K`1lanO5E@TWUMbePV&hhy#L5XyF233k z!>#Sn+|nAw$;Hhrk(}~zQxl;I=R!5(%N2|ZHB2tlg~$EtpAu+4?SZksW3TTA+nfca literal 0 HcmV?d00001 diff --git a/tools/MouseJump.Kicker/KickerForm.Designer.cs b/tools/MouseJump.Kicker/KickerForm.Designer.cs new file mode 100644 index 0000000000..fa07d2d67e --- /dev/null +++ b/tools/MouseJump.Kicker/KickerForm.Designer.cs @@ -0,0 +1,96 @@ +// 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 System.Drawing; +using System.Windows.Forms; + +namespace MouseJump.Kicker +{ + partial class KickerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(KickerForm)); + ActivationHotkey = new Button(); + CloseMouseJump = new Button(); + StartWinUI3 = new Button(); + SuspendLayout(); + // + // ActivationHotkey + // + ActivationHotkey.Location = new Point(12, 89); + ActivationHotkey.Name = "ActivationHotkey"; + ActivationHotkey.Size = new Size(260, 70); + ActivationHotkey.TabIndex = 1; + ActivationHotkey.Text = "Activate Hotkey"; + ActivationHotkey.UseVisualStyleBackColor = true; + ActivationHotkey.Click += ActivationHotkey_Click; + // + // CloseMouseJump + // + CloseMouseJump.Location = new Point(12, 165); + CloseMouseJump.Name = "CloseMouseJump"; + CloseMouseJump.Size = new Size(260, 70); + CloseMouseJump.TabIndex = 2; + CloseMouseJump.Text = "Close MouseJump"; + CloseMouseJump.UseVisualStyleBackColor = true; + CloseMouseJump.Click += CloseMouseJump_Click; + // + // StartWinUI3 + // + StartWinUI3.Location = new Point(12, 12); + StartWinUI3.Name = "StartWinUI3"; + StartWinUI3.Size = new Size(260, 70); + StartWinUI3.TabIndex = 3; + StartWinUI3.Text = "Start MouseJump (WinUI3)"; + StartWinUI3.UseVisualStyleBackColor = true; + StartWinUI3.Click += StartWinUI3_Click; + // + // KickerForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(281, 242); + Controls.Add(StartWinUI3); + Controls.Add(CloseMouseJump); + Controls.Add(ActivationHotkey); + FormBorderStyle = FormBorderStyle.FixedSingle; + Icon = (Icon)resources.GetObject("$this.Icon"); + MaximizeBox = false; + MinimizeBox = false; + Name = "KickerForm"; + Text = "MouseJump Kicker"; + ResumeLayout(false); + } + + #endregion + private Button ActivationHotkey; + private Button CloseMouseJump; + private Button StartWinUI3; + } +} diff --git a/tools/MouseJump.Kicker/KickerForm.cs b/tools/MouseJump.Kicker/KickerForm.cs new file mode 100644 index 0000000000..8b93faa132 --- /dev/null +++ b/tools/MouseJump.Kicker/KickerForm.cs @@ -0,0 +1,131 @@ +// 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 System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +using MouseJump.Kicker.NativeMethods; + +using static MouseJump.Kicker.NativeMethods.Core; +using static MouseJump.Kicker.NativeMethods.Kernel32; +using static MouseJump.Kicker.NativeMethods.User32; + +namespace MouseJump.Kicker; + +public partial class KickerForm : Form +{ + public KickerForm() + { + InitializeComponent(); + } + + private Process? Process + { + get; + set; + } + + private void StartWinUI3_Click(object sender, EventArgs e) + { + // this project builds into "{RepoRoot}\tools\MouseJump.Kicker\bin\x64\Debug" + var kickerDir = AppContext.BaseDirectory; + + // step up to the repo root + var repoRoot = Path.Combine(kickerDir, "..", "..", "..", "..", ".."); + + // and back down to the mouse jump exe + var mouseJumpExe = Path.Combine(repoRoot, "x64", "Debug", "WinUI3Apps", "PowerToys.MouseJump.WinUI3.exe"); + + this.Process = this.StartMouseJump(mouseJumpExe); + } + + private Process StartMouseJump(string mouseJumpExe) + { + if (!File.Exists(mouseJumpExe)) + { + throw new FileNotFoundException($"Could not find {mouseJumpExe}"); + } + + var args = new List + { + Environment.ProcessId.ToString(CultureInfo.InvariantCulture), + }; + return Process.Start(mouseJumpExe, args); + } + + private void ActivationHotkey_Click(object sender, EventArgs e) + { + // find the winui3 window and call SetForegroundWindow on it before signalling + // the event. this mirrors the approach used in on_hotkey() in + // src/modules/MouseUtils/MouseJump/dllmain.cpp - see the comment there for a + // full explanation. + // + // in brief: the kicker is the foreground process right now (the user just + // clicked the button), so it has permission to call SetForegroundWindow and + // transfer foreground status directly to the winui3 window. doing this before + // signalling the event means that by the time ShowWindowAsync() calls + // SetForegroundWindow on its own window, the winui3 process already owns the + // foreground and the call succeeds. + // + // using AllowSetForegroundWindow here instead would be unreliable — the + // permission it grants is revoked as soon as the user generates any input + // (e.g. moving the mouse), which is almost certain to happen while the + // preview is rendering. + if (this.Process is not null) + { + var targetPid = (uint)this.Process.Id; + var winUI3Hwnd = HWND.Null; + EnumWindows( + (hwnd, _) => + { + GetWindowThreadProcessId(hwnd, out var windowPid); + if (windowPid == targetPid) + { + winUI3Hwnd = hwnd; + return false; // stop enumeration + } + + return true; // continue enumeration + }, + LPARAM.Zero); + if (!winUI3Hwnd.IsNull) + { + SetForegroundWindow(winUI3Hwnd); + } + } + + const string MOUSE_JUMP_SHOW_PREVIEW_EVENT = "Local\\MouseJumpEvent-aa0be051-3396-4976-b7ba-1a9cc7d236a5"; + var attributes = new LPSECURITY_ATTRIBUTES( + new SECURITY_ATTRIBUTES( + (uint)Marshal.SizeOf(), + LPVOID.Null, + false)); + var hEvent = Kernel32.CreateEventW( + attributes, + false, + false, + MOUSE_JUMP_SHOW_PREVIEW_EVENT); + if (hEvent.IsNull) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + var result = Kernel32.SetEvent(hEvent); + if (!result) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + + private void CloseMouseJump_Click(object sender, EventArgs e) + { + this.Process?.Kill(); + } +} diff --git a/tools/MouseJump.Kicker/KickerForm.resx b/tools/MouseJump.Kicker/KickerForm.resx new file mode 100644 index 0000000000..4f7675806d --- /dev/null +++ b/tools/MouseJump.Kicker/KickerForm.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAMd2C0LGdQpWxHMJVMRyEFS6ZABUsE4AVcBsA1S+awhUvWoGVLxpBlS8aQZUvGkGVLxp + BlS8aQZUvGkGVrxpBkLKegz/yXgL/8h3C//GcwD/xIta/8aljP+7aAD/wnAI/8BuCP+/bQf/vmsH/71q + B/+9agb/vGkG/7xpBv+8aQb/0IAO/85/Df/Nfg3/ynUA/9SqhP/w8O7/yKGD/8JsAP/HdQ3/xXMK/8Ny + Cf/CcQn/wW8I/8BtCP++bAf/vmsH/9WHEP/UhhD/04QP/9F9AP/Vpnj/8/X1/+zp5v/EjVv/yXYA/8t6 + Dv/JeAv/yHcL/8Z1Cv/FdAr/xHMK/8NxCf/bjhL/2owS/9mLE//TgAD/0KV5//Pz8//3+fr/5t3U/8V/ + HP/QfgD/zn8O/81+Df/MfAz/y3sM/8p5DP/IeAv/35QV/9+TFv/ZiwD/ypZb/+Pc1f/19fb/9vb2//r7 + /P/fzb3/y34A/9SFCv/ThBD/0oMG/9GAAP/QgAz/zn4O/+KZGP/hlgD/16dv/+nn5P/09/n/9ff4//f5 + +v/6/P3/+/7//9y4kv/YhwD/14kA/8qAAP/Hhzj/zH4A/9SFD//knhz/4poA/+S3e//s2L//69O0/+zT + s//s0K3/7tGu/+7Pq//lsXT/3o8A/9WTOv/n2s7/9PHs/9zCqv/ThQD/5qMg/+WiIP/loAr/45wA/+Ob + AP/jmgD/4pkA/+GXAP/hlQD/4ZcA/+CTAP/kuYf////////////59Oz/25k7/+epJP/npyL/56Yi/+al + If/mpCD/5aMf/+WhHv/koB3/5J8d/+SfHP/jmwD/5a1f//z27v//////9OPP/+CYBP/qrif/6a0m/+ms + Jf/oqyX/6Kkk/+ioJP/npyP/56Yi/+alIf/mpCD/5aMf/+SfCP/osV//7MGH/+SkPP/jnA7/67EoQuqv + KFbqryhU6q4nVOqtJ1TprCZU6KolVOipJFTnqCNU56ciVOamIlTmpSFU5JwAVOKVAFTkngBW5KAhQgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA//+sQf//rEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAA + rEH//6xB//+sQQ== + + + \ No newline at end of file diff --git a/tools/MouseJump.Kicker/MouseJump.Kicker.csproj b/tools/MouseJump.Kicker/MouseJump.Kicker.csproj new file mode 100644 index 0000000000..7bb8230623 --- /dev/null +++ b/tools/MouseJump.Kicker/MouseJump.Kicker.csproj @@ -0,0 +1,20 @@ + + + + + + + PowerToys.MouseJump.Kicker + PowerToys.MouseJump.Kicker + PowerToys MouseJump.Kicker + WinExe + false + false + + PerMonitorV2 + true + enable + true + disable + + diff --git a/tools/MouseJump.Kicker/MouseJump.Kicker.slnx b/tools/MouseJump.Kicker/MouseJump.Kicker.slnx new file mode 100644 index 0000000000..e13efe43ad --- /dev/null +++ b/tools/MouseJump.Kicker/MouseJump.Kicker.slnx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs b/tools/MouseJump.Kicker/NativeMethods/Core/BOOL.cs similarity index 96% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs rename to tools/MouseJump.Kicker/NativeMethods/Core/BOOL.cs index 293ae0a226..0fc04e0d17 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Core/BOOL.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs b/tools/MouseJump.Kicker/NativeMethods/Core/DWORD.cs similarity index 81% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs rename to tools/MouseJump.Kicker/NativeMethods/Core/DWORD.cs index 5a7ae72697..aab1403afc 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Core/DWORD.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; internal static partial class Core { @@ -26,16 +26,12 @@ internal static partial class Core } public static int Size => - Marshal.SizeOf(typeof(DWORD)); + Marshal.SizeOf(); public static implicit operator uint(DWORD value) => value.Value; public static implicit operator DWORD(uint value) => new(value); - public static explicit operator int(DWORD value) => (int)value.Value; - - public static explicit operator DWORD(int value) => new((uint)value); - public override string ToString() { return $"{this.GetType().Name}({this.Value})"; diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs b/tools/MouseJump.Kicker/NativeMethods/Core/HANDLE.cs similarity index 94% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs rename to tools/MouseJump.Kicker/NativeMethods/Core/HANDLE.cs index d32f6f8e3c..d78a7b4216 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Core/HANDLE.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.NativeMethods; +using System; + +namespace MouseJump.Kicker.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs b/tools/MouseJump.Kicker/NativeMethods/Core/HWND.cs similarity index 71% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs rename to tools/MouseJump.Kicker/NativeMethods/Core/HWND.cs index 715f922b66..f3861bee17 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Core/HWND.cs @@ -1,10 +1,10 @@ -// Copyright (c) Microsoft Corporation +// 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 System.Diagnostics.CodeAnalysis; +using System; -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; internal static partial class Core { @@ -20,9 +20,6 @@ internal static partial class Core { public static readonly HWND Null = new(IntPtr.Zero); - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Name and value taken from Win32Api")] - public static readonly HWND HWND_MESSAGE = new(-3); - public readonly IntPtr Value; public HWND(IntPtr value) @@ -32,6 +29,10 @@ internal static partial class Core public bool IsNull => this.Value == HWND.Null.Value; + public static implicit operator IntPtr(HWND value) => value.Value; + + public static explicit operator HWND(IntPtr value) => new(value); + public override string ToString() { return $"{this.GetType().Name}({this.Value})"; diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs b/tools/MouseJump.Kicker/NativeMethods/Core/LPARAM.cs similarity index 58% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs rename to tools/MouseJump.Kicker/NativeMethods/Core/LPARAM.cs index f93e089b12..53cae9bec8 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Core/LPARAM.cs @@ -1,13 +1,13 @@ -// Copyright (c) Microsoft Corporation +// 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. -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; internal static partial class Core { /// - /// A message parameter. + /// A message parameter. This type contains information about a message. /// This type is declared in WinDef.h as follows: /// typedef LONG_PTR LPARAM; /// @@ -16,20 +16,18 @@ internal static partial class Core /// internal readonly struct LPARAM { - public static readonly LPARAM Null = new(IntPtr.Zero); + public static readonly LPARAM Zero = new(0); - public readonly IntPtr Value; + public readonly nint Value; - public LPARAM(IntPtr value) + public LPARAM(nint value) { this.Value = value; } - public bool IsNull => this.Value == LPARAM.Null.Value; + public static implicit operator nint(LPARAM value) => value.Value; - public static implicit operator IntPtr(LPARAM value) => value.Value; - - public static explicit operator LPARAM(IntPtr value) => new(value); + public static explicit operator LPARAM(nint value) => new(value); public override string ToString() { diff --git a/tools/MouseJump.Kicker/NativeMethods/Core/LPCWSTR.cs b/tools/MouseJump.Kicker/NativeMethods/Core/LPCWSTR.cs new file mode 100644 index 0000000000..8bc8eb5518 --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/Core/LPCWSTR.cs @@ -0,0 +1,56 @@ +// 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 System; +using System.Runtime.InteropServices; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class Core +{ + /// + /// A pointer to a constant null-terminated string of 16-bit Unicode characters.For more information, see Character Sets Used By Fonts. + /// This type is declared in WinNT.h as follows: + /// typedef CONST WCHAR* LPCWSTR; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LPCWSTR + { + public static readonly LPCWSTR Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPCWSTR(IntPtr value) + { + this.Value = value; + } + + public LPCWSTR(string value) + { + this.Value = Marshal.StringToHGlobalAuto(value); + } + + public bool IsNull => this.Value == LPCWSTR.Null.Value; + + public static implicit operator IntPtr(LPCWSTR value) => value.Value; + + public static explicit operator LPCWSTR(IntPtr value) => new(value); + + public static implicit operator string?(LPCWSTR value) => Marshal.PtrToStringUni(value.Value); + + public static implicit operator LPCWSTR(string value) => new(value); + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/tools/MouseJump.Kicker/NativeMethods/Core/LPVOID.cs b/tools/MouseJump.Kicker/NativeMethods/Core/LPVOID.cs new file mode 100644 index 0000000000..4bdb41b72c --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/Core/LPVOID.cs @@ -0,0 +1,56 @@ +// 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 System; +using System.Runtime.InteropServices; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class Core +{ + /// + /// A pointer to any type. + /// This type is declared in WinDef.h as follows: + /// typedef void* LPVOID; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LPVOID + { + public static readonly LPVOID Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPVOID(IntPtr value) + { + this.Value = value; + } + + public static implicit operator IntPtr(LPVOID value) => value.Value; + + public static explicit operator LPVOID(IntPtr value) => new(value); + + public static LPVOID Allocate(int length) + { + var ptr = Marshal.AllocHGlobal(length); + return new LPVOID(ptr); + } + + public string? PtrToStringUni() + { + return Marshal.PtrToStringUni(this.Value); + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.LPSECURITY_ATTRIBUTES.cs b/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.LPSECURITY_ATTRIBUTES.cs new file mode 100644 index 0000000000..4a7306bed3 --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.LPSECURITY_ATTRIBUTES.cs @@ -0,0 +1,56 @@ +// 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 System; +using System.Runtime.InteropServices; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class Kernel32 +{ + internal readonly struct LPSECURITY_ATTRIBUTES + { + public static readonly LPSECURITY_ATTRIBUTES Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPSECURITY_ATTRIBUTES(IntPtr value) + { + this.Value = value; + } + + public LPSECURITY_ATTRIBUTES(SECURITY_ATTRIBUTES value) + { + this.Value = LPSECURITY_ATTRIBUTES.ToPtr(value); + } + + public bool IsNull => this.Value == LPSECURITY_ATTRIBUTES.Null.Value; + + private static IntPtr ToPtr(SECURITY_ATTRIBUTES value) + { + var ptr = Marshal.AllocHGlobal(SECURITY_ATTRIBUTES.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public SECURITY_ATTRIBUTES ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPSECURITY_ATTRIBUTES value) => value.Value; + + public static implicit operator LPSECURITY_ATTRIBUTES(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.SECURITY_ATTRIBUTES.cs b/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.SECURITY_ATTRIBUTES.cs new file mode 100644 index 0000000000..fb27ea7b42 --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/Kernel32/Security/Kernel32.SECURITY_ATTRIBUTES.cs @@ -0,0 +1,34 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +using static MouseJump.Kicker.NativeMethods.Core; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class Kernel32 +{ + [SuppressMessage("SA1304", "SA1304:NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")] + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct SECURITY_ATTRIBUTES + { + internal readonly DWORD nLength; + internal readonly LPVOID lpSecurityDescriptor; + internal readonly BOOL bInheritHandle; + + internal SECURITY_ATTRIBUTES( + DWORD nLength, LPVOID lpSecurityDescriptor, BOOL bInheritHandle) + { + this.nLength = nLength; + this.lpSecurityDescriptor = lpSecurityDescriptor; + this.bInheritHandle = bInheritHandle; + } + + public static int Size => + Marshal.SizeOf(); + } +} diff --git a/tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.CreateEventW.cs b/tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.CreateEventW.cs new file mode 100644 index 0000000000..40489c6843 --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.CreateEventW.cs @@ -0,0 +1,32 @@ +// 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 System.Runtime.InteropServices; +using static MouseJump.Kicker.NativeMethods.Core; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class Kernel32 +{ + /// + /// Creates or opens a named or unnamed event object. + /// To specify an access mask for the object, use the CreateEventEx function. + /// + /// + /// If the function succeeds, the return value is a handle to the event object. + /// If the named event object existed before the function call, the function returns + /// a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. + /// If the function fails, the return value is NULL. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + /// + [LibraryImport(Libraries.Kernel32, StringMarshalling = StringMarshalling.Utf16, SetLastError = true)] + internal static partial HANDLE CreateEventW( + LPSECURITY_ATTRIBUTES lpEventAttributes, + BOOL bManualReset, + BOOL bInitialState, + LPCWSTR lpName); +} diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs b/tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.SetEvent.cs similarity index 59% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs rename to tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.SetEvent.cs index cd328f76cc..f03f4cc712 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Kernel32/SystemServices/Kernel32.SetEvent.cs @@ -4,24 +4,24 @@ using System.Runtime.InteropServices; -using static MouseJump.Common.NativeMethods.Core; +using static MouseJump.Kicker.NativeMethods.Core; -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; -internal static partial class User32 +internal static partial class Kernel32 { /// - /// The GetMonitorInfo function retrieves information about a display monitor. + /// Sets the specified event object to the signaled state. /// /// /// If the function succeeds, the return value is nonzero. /// If the function fails, the return value is zero. + /// To get extended error information, call GetLastError. /// /// /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors /// - [LibraryImport(Libraries.User32)] - internal static partial BOOL GetMonitorInfoW( - HMONITOR hMonitor, - LPMONITORINFO lpmi); + [LibraryImport(Libraries.Kernel32, SetLastError = true)] + internal static partial Core.BOOL SetEvent( + HANDLE hEvent); } diff --git a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs b/tools/MouseJump.Kicker/NativeMethods/Libraries.cs similarity index 69% rename from src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs rename to tools/MouseJump.Kicker/NativeMethods/Libraries.cs index 970183cab0..aadb6c596d 100644 --- a/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs +++ b/tools/MouseJump.Kicker/NativeMethods/Libraries.cs @@ -2,10 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJump.Common.NativeMethods; +namespace MouseJump.Kicker.NativeMethods; internal static class Libraries { public const string Gdi32 = "gdi32"; + public const string Kernel32 = "kernel32"; + public const string Shell32 = "shell32"; public const string User32 = "user32"; } diff --git a/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.EnumWindows.cs b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.EnumWindows.cs new file mode 100644 index 0000000000..ab15ce2e6b --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.EnumWindows.cs @@ -0,0 +1,38 @@ +// 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 System.Runtime.InteropServices; + +using static MouseJump.Kicker.NativeMethods.Core; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class User32 +{ + /// + /// An application-defined callback function for use with EnumWindows. + /// Return TRUE to continue enumeration, FALSE to stop. + /// + /// + /// See https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498(v=vs.85) + /// + internal delegate BOOL EnumWindowsProc(HWND hwnd, LPARAM lParam); + + /// + /// Enumerates all top-level windows on the screen, including hidden windows, + /// by passing the handle to each window in turn to an application-defined callback function. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows + /// + [DllImport(Libraries.User32, SetLastError = true)] + internal static extern BOOL EnumWindows( + EnumWindowsProc lpEnumFunc, + LPARAM lParam); +} diff --git a/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.GetWindowThreadProcessId.cs b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.GetWindowThreadProcessId.cs new file mode 100644 index 0000000000..7f431b6efd --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.GetWindowThreadProcessId.cs @@ -0,0 +1,27 @@ +// 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 System.Runtime.InteropServices; + +using static MouseJump.Kicker.NativeMethods.Core; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves the identifier of the thread that created the specified window and, + /// optionally, the identifier of the process that created the window. + /// + /// + /// The return value is the identifier of the thread that created the window. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial uint GetWindowThreadProcessId( + HWND hWnd, + out uint lpdwProcessId); +} diff --git a/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.SetForegroundWindow.cs b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.SetForegroundWindow.cs new file mode 100644 index 0000000000..b39e464269 --- /dev/null +++ b/tools/MouseJump.Kicker/NativeMethods/User32/WindowsAndMessaging/User32.SetForegroundWindow.cs @@ -0,0 +1,27 @@ +// 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 System.Runtime.InteropServices; + +using static MouseJump.Kicker.NativeMethods.Core; + +namespace MouseJump.Kicker.NativeMethods; + +internal static partial class User32 +{ + /// + /// Brings the thread that created the specified window into the foreground and + /// activates the window. + /// + /// + /// If the window was brought to the foreground, the return value is nonzero. + /// If the window was not brought to the foreground, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial BOOL SetForegroundWindow( + HWND hWnd); +} diff --git a/tools/MouseJump.Kicker/Program.cs b/tools/MouseJump.Kicker/Program.cs new file mode 100644 index 0000000000..9ba2279dc3 --- /dev/null +++ b/tools/MouseJump.Kicker/Program.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 System; +using System.Windows.Forms; + +namespace MouseJump.Kicker; + +internal static class Program +{ + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new KickerForm()); + } +} diff --git a/tools/MouseJump.Kicker/readme.md b/tools/MouseJump.Kicker/readme.md new file mode 100644 index 0000000000..323133d633 --- /dev/null +++ b/tools/MouseJump.Kicker/readme.md @@ -0,0 +1,11 @@ +## MouseJump.Kicker + +This is a simple "kicker" app that can be used to launch the Mouse Jump background process +and activate the hotkey without needing to build and run the entire "runner" app. + +It's intended to be used for testing and development purposes, and is not meant to be +included in the main PowerToys release footprint. + +When launching Mouse Jump via the kicker, it will look in the build output folder and +launch the last successful build. Make sure to build the Mouse Jump project before +launching the kicker - otherwise it won't run the latest version of the code. \ No newline at end of file