From fda75e48d5697ea79d73b80bf121f7ab521414e0 Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Mon, 24 Apr 2023 16:15:07 +0100 Subject: [PATCH] [MouseJump]Reduce winforms dependency, thumbnail size settings, shortcut keys(#25487) * [Mouse Jump] - reorganise existing NativeMethods (#25482) * Mouse Jump] - reorganise Helper classes / main form code (#25482) * Mouse Jump] - replace use of System.Windows.Forms.Screen with Native Methods (#25482) * Mouse Jump] - replace use of System.Windows.Forms.SystemInformation with Native Methods (#25482) * [Mouse Jump] - replace use of System.Windows.Forms.Cursor with Native Methods (#25482) * [Mouse Jump] - improve popup responsiveness (#25484) * [Mouse Jump] - fixed spellchecker errors (#25484) * [Mouse Jump] - add settings card for thumbnail size (#24564) * [Mouse Jump] - shortcut keys to jump to centres of screens (#25069) * [Mouse Jump] - fix spelling (#25069) * [Mouse Jump] - fix spelling - numpad (#25069) * [Mouse Jump] - updated "thumbnail size" settings text (#24564) --- .github/actions/spell-check/excludes.txt | 1 + .github/actions/spell-check/expect.txt | 11 +- .../Helpers/DrawingHelperTests.cs | 72 +++--- .../Helpers/MouseHelperTests.cs | 6 +- .../Drawing/RectangleInfoTests.cs | 12 +- .../{ => Models}/Drawing/SizeInfoTests.cs | 13 +- .../MouseJumpUI/Helpers/DrawingHelper.cs | 131 +++------- .../MouseJumpUI/Helpers/LayoutHelper.cs | 92 +++++++ .../MouseJumpUI/Helpers/MouseHelper.cs | 96 +++++-- .../MouseJumpUI/Helpers/NativeMethods.cs | 111 -------- .../MouseJumpUI/Helpers/ScreenHelper.cs | 94 +++++++ .../MouseUtils/MouseJumpUI/MainForm.cs | 243 +++++++++++++----- .../Models => Models/Drawing}/PaddingInfo.cs | 7 +- .../Models => Models/Drawing}/PointInfo.cs | 7 +- .../Drawing}/RectangleInfo.cs | 10 +- .../Models => Models/Drawing}/SizeInfo.cs | 2 +- .../Models => Models/Layout}/LayoutConfig.cs | 67 +++-- .../Models => Models/Layout}/LayoutInfo.cs | 13 +- .../MouseJumpUI/Models/Screen/ScreenInfo.cs | 46 ++++ .../MouseUtils/MouseJumpUI/MouseJumpUI.csproj | 1 + .../MouseJumpUI/NativeMethods/Core/BOOL.cs | 62 +++-- .../MouseJumpUI/NativeMethods/Core/CRECT.cs | 45 ++++ .../MouseJumpUI/NativeMethods/Core/DWORD.cs | 35 +++ .../MouseJumpUI/NativeMethods/Core/HANDLE.cs | 41 +++ .../MouseJumpUI/NativeMethods/Core/HDC.cs | 44 ++-- .../NativeMethods/Core/HMONITOR.cs | 49 ++++ .../MouseJumpUI/NativeMethods/Core/HWND.cs | 44 ++-- .../MouseJumpUI/NativeMethods/Core/LONG.cs | 35 +++ .../MouseJumpUI/NativeMethods/Core/LPARAM.cs | 38 +++ .../MouseJumpUI/NativeMethods/Core/LPCRECT.cs | 49 ++++ .../MouseJumpUI/NativeMethods/Core/LPPOINT.cs | 54 ++++ .../MouseJumpUI/NativeMethods/Core/LPRECT.cs | 48 ++++ .../MouseJumpUI/NativeMethods/Core/POINT.cs | 47 ++++ .../MouseJumpUI/NativeMethods/Core/RECT.cs | 45 ++++ .../MouseJumpUI/NativeMethods/Core/UINT.cs | 34 +++ .../NativeMethods/Core/ULONG_PTR.cs | 44 ++++ .../MouseJumpUI/NativeMethods/Core/WORD.cs | 35 +++ .../{ => Graphics/Gdi}/Gdi32.ROP_CODE.cs | 2 +- .../Gdi}/Gdi32.STRETCH_BLT_MODE.cs | 2 +- .../Gdi}/Gdi32.SetStretchBltMode.cs | 4 +- .../{ => Graphics/Gdi}/Gdi32.StretchBlt.cs | 4 +- .../Gdi/User32.EnumDisplayMonitors.cs | 33 +++ .../Graphics/Gdi/User32.GetMonitorInfoW.cs | 26 ++ .../{ => Graphics/Gdi}/User32.GetWindowDC.cs | 4 +- .../Graphics/Gdi/User32.LPMONITORINFO.cs | 53 ++++ .../Graphics/Gdi/User32.MONITORENUMPROC .cs | 22 ++ .../User32/Graphics/Gdi/User32.MONITORINFO.cs | 39 +++ .../Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs | 24 ++ .../Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs | 22 ++ .../Graphics/Gdi/User32.MonitorFromPoint .cs | 26 ++ .../{ => Graphics/Gdi}/User32.ReleaseDC.cs | 4 +- .../KeyboardAndMouse/User32.HARDWAREINPUT.cs | 38 +++ .../UI/Input/KeyboardAndMouse/User32.INPUT.cs | 51 ++++ .../KeyboardAndMouse/User32.INPUT_TYPE.cs | 24 ++ .../KeyboardAndMouse/User32.KEYBDINPUT.cs | 43 ++++ .../Input/KeyboardAndMouse/User32.LPINPUT.cs | 72 ++++++ .../KeyboardAndMouse/User32.MOUSEINPUT.cs | 46 ++++ .../User32.MOUSE_EVENT_FLAGS.cs | 34 +++ .../KeyboardAndMouse/User32.SendInput.cs | 28 ++ .../User32.GetCursorPos.cs | 25 ++ .../User32.GetDesktopWindow.cs | 4 +- .../User32.GetSystemMetrics.cs | 26 ++ .../User32.SYSTEM_METRICS_INDEX.cs | 110 ++++++++ .../User32.SetCursorPos.cs | 28 ++ .../Gdi32/Gdi32.SetStretchBltMode.cs | 21 -- .../NativeWrappers/Gdi32/Gdi32.StretchBlt.cs | 43 ---- .../User32/User32.GetDesktopWindow.cs | 16 -- .../User32/User32.GetWindowDC.cs | 21 -- .../NativeWrappers/User32/User32.ReleaseDC.cs | 21 -- src/modules/MouseUtils/MouseJumpUI/Program.cs | 35 ++- .../MouseJumpProperties.cs | 4 + .../MouseJumpThumbnailSize.cs | 68 +++++ .../Settings.UI/Strings/en-us/Resources.resw | 17 ++ .../ViewModels/MouseUtilsViewModel.cs | 25 ++ .../Settings.UI/Views/MouseUtilsPage.xaml | 75 ++++++ 75 files changed, 2336 insertions(+), 588 deletions(-) rename src/modules/MouseUtils/MouseJumpUI.UnitTests/{ => Models}/Drawing/RectangleInfoTests.cs (95%) rename src/modules/MouseUtils/MouseJumpUI.UnitTests/{ => Models}/Drawing/SizeInfoTests.cs (93%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Drawing}/PaddingInfo.cs (88%) rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Drawing}/PointInfo.cs (89%) rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Drawing}/RectangleInfo.cs (86%) rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Drawing}/SizeInfo.cs (98%) rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Layout}/LayoutConfig.cs (51%) rename src/modules/MouseUtils/MouseJumpUI/{Drawing/Models => Models/Layout}/LayoutInfo.cs (85%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/{ => Graphics/Gdi}/Gdi32.ROP_CODE.cs (97%) rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/{ => Graphics/Gdi}/Gdi32.STRETCH_BLT_MODE.cs (95%) rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/{ => Graphics/Gdi}/Gdi32.SetStretchBltMode.cs (90%) rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/{ => Graphics/Gdi}/Gdi32.StretchBlt.cs (93%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/{ => Graphics/Gdi}/User32.GetWindowDC.cs (93%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/{ => Graphics/Gdi}/User32.ReleaseDC.cs (92%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs rename src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/{ => UI/WindowsAndMessaging}/User32.GetDesktopWindow.cs (88%) create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs create mode 100644 src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index fd107c3f77..3e04a7f21f 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -99,6 +99,7 @@ ^src/modules/fancyzones/lib/FancyZonesWinHookEventIDs\.h$ ^src/modules/imageresizer/dll/ContextMenuHandler\.rgs$ ^src/modules/imageresizer/dll/ImageResizerExt\.rgs$ +^src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs$ ^src/modules/powerrename/testapp/PowerRenameTest\.vcxproj\.filters$ ^src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithHTMLImageTag\.txt$ ^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag.txt$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 419d345e17..5f5c563b8d 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -358,6 +358,7 @@ createdump CREATESCHEDULEDTASK CREATESTRUCT CREATEWINDOWFAILED +CRECT critsec Crossdevice CRSEL @@ -512,6 +513,7 @@ dreamsofameaningfullife drivedetectionwarning dshow DSTINVERT +DUMMYUNIONNAME dutil DVASPECT DVASPECTINFO @@ -1064,14 +1066,18 @@ LPBYTE LPCITEMIDLIST LPCMINVOKECOMMANDINFO LPCREATESTRUCT +LPCRECT LPCTSTR LPCWSTR lpdw lpfn +lpmi LPINPUT LPMINMAXINFO +LPMONITORINFO LPOSVERSIONINFOEXW lprc +LPPOINT LPRECT LPSAFEARRAY LPSTR @@ -1181,6 +1187,7 @@ mockapi MODECHANGE modernwpf MODESPRUNED +MONITORENUMPROC MONITORINFO MONITORINFOEX MONITORINFOEXW @@ -1334,7 +1341,7 @@ nugets nullonfailure numberbox NUMLOCK -NUMPAD +numpad nwc Objbase OBJID @@ -1971,6 +1978,7 @@ TRAYMOUSEMESSAGE triaging TRK trl +Tsd TServer TStr TValue @@ -2174,6 +2182,7 @@ winternl WINTHRESHOLD winui winuiex +WINVER winxamlmanager wistd withinrafael diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs index cf2fb5562a..7579780ebc 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Drawing; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Drawing.Models; using MouseJumpUI.Helpers; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; +using MouseJumpUI.Models.Screen; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.UnitTests.Helpers; @@ -14,9 +16,9 @@ namespace MouseJumpUI.UnitTests.Helpers; public static class DrawingHelperTests { [TestClass] - public class CalculateLayoutInfoTests + public sealed class CalculateLayoutInfoTests { - public class TestCase + public sealed class TestCase { public TestCase(LayoutConfig layoutConfig, LayoutInfo expectedResult) { @@ -40,13 +42,14 @@ public static class DrawingHelperTests // | | // +----------------+ var layoutConfig = new LayoutConfig( - virtualScreen: new(0, 0, 5120, 1440), - screenBounds: new List + virtualScreenBounds: new(0, 0, 5120, 1440), + screens: new List { - new(0, 0, 5120, 1440), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 5120, 1440), new(0, 0, 5120, 1440)), }, activatedLocation: new(5120 / 2, 1440 / 2), - activatedScreen: 0, + activatedScreenIndex: 0, + activatedScreenNumber: 1, maximumFormSize: new(1600, 1200), formPadding: new(5, 5, 5, 5), previewPadding: new(0, 0, 0, 0)); @@ -58,7 +61,7 @@ public static class DrawingHelperTests { new(0, 0, 1590, 447.1875M), }, - activatedScreen: new(0, 0, 5120, 1440)); + activatedScreenBounds: new(0, 0, 5120, 1440)); yield return new[] { new TestCase(layoutConfig, layoutInfo) }; // primary monitor not topmost / leftmost - if there are screens @@ -74,14 +77,15 @@ public static class DrawingHelperTests // | | // +----------------+ layoutConfig = new LayoutConfig( - virtualScreen: new(-1920, -472, 7040, 1912), - screenBounds: new List + virtualScreenBounds: new(-1920, -472, 7040, 1912), + screens: new List { - new(-1920, -472, 1920, 1080), - new(0, 0, 5120, 1440), + new ScreenInfo(HMONITOR.Null, false, new(-1920, -472, 1920, 1080), new(-1920, -472, 1920, 1080)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 5120, 1440), new(0, 0, 5120, 1440)), }, activatedLocation: new(-960, -236), - activatedScreen: 0, + activatedScreenIndex: 0, + activatedScreenNumber: 1, maximumFormSize: new(1600, 1200), formPadding: new(5, 5, 5, 5), previewPadding: new(0, 0, 0, 0)); @@ -99,7 +103,7 @@ public static class DrawingHelperTests new(0, 0, 433.63636M, 243.92045M), new(433.63636M, 106.602270M, 1156.36363M, 325.22727M), }, - activatedScreen: new(-1920, -472, 1920, 1080)); + activatedScreenBounds: new(-1920, -472, 1920, 1080)); yield return new[] { new TestCase(layoutConfig, layoutInfo) }; // check we handle rounding errors in scaling the preview form @@ -115,14 +119,15 @@ public static class DrawingHelperTests // | | 0 | // +----------------+-------+ layoutConfig = new LayoutConfig( - virtualScreen: new(0, 0, 7168, 1440), - screenBounds: new List + virtualScreenBounds: new(0, 0, 7168, 1440), + screens: new List { - new(6144, 0, 1024, 768), - new(0, 0, 6144, 1440), + new ScreenInfo(HMONITOR.Null, false, new(6144, 0, 1024, 768), new(6144, 0, 1024, 768)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)), }, activatedLocation: new(6656, 384), - activatedScreen: 0, + activatedScreenIndex: 0, + activatedScreenNumber: 1, maximumFormSize: new(1600, 1200), formPadding: new(5, 5, 5, 5), previewPadding: new(0, 0, 0, 0)); @@ -135,7 +140,7 @@ public static class DrawingHelperTests new(869.14285M, 0, 144.85714M, 108.642857M), new(0, 0, 869.142857M, 203.705357M), }, - activatedScreen: new(6144, 0, 1024, 768)); + activatedScreenBounds: new(6144, 0, 1024, 768)); yield return new[] { new TestCase(layoutConfig, layoutInfo) }; // check we handle rounding errors in scaling the preview form @@ -151,14 +156,15 @@ public static class DrawingHelperTests // | | 0 | // +----------------+-------+ layoutConfig = new LayoutConfig( - virtualScreen: new(0, 0, 7424, 1440), - screenBounds: new List + virtualScreenBounds: new(0, 0, 7424, 1440), + screens: new List { - new(6144, 0, 1280, 768), - new(0, 0, 6144, 1440), + new ScreenInfo(HMONITOR.Null, false, new(6144, 0, 1280, 768), new(6144, 0, 1280, 768)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)), }, activatedLocation: new(6784, 384), - activatedScreen: 0, + activatedScreenIndex: 0, + activatedScreenNumber: 1, maximumFormSize: new(1600, 1200), formPadding: new(5, 5, 5, 5), previewPadding: new(0, 0, 0, 0)); @@ -176,7 +182,7 @@ public static class DrawingHelperTests new(1051.03448M, 0, 218.96551M, 131.37931M), new(0, 0M, 1051.03448M, 246.33620M), }, - activatedScreen: new(6144, 0, 1280, 768)); + activatedScreenBounds: new(6144, 0, 1280, 768)); yield return new[] { new TestCase(layoutConfig, layoutInfo) }; } @@ -191,7 +197,7 @@ public static class DrawingHelperTests // (int)1280.000000000000 -> 1280 // so we'll compare the raw values, *and* convert to an int-based // Rectangle to compare rounded values - var actual = DrawingHelper.CalculateLayoutInfo(data.LayoutConfig); + var actual = LayoutHelper.CalculateLayoutInfo(data.LayoutConfig); var expected = data.ExpectedResult; Assert.AreEqual(expected.FormBounds.X, actual.FormBounds.X, 0.00001M, "FormBounds.X"); Assert.AreEqual(expected.FormBounds.Y, actual.FormBounds.Y, 0.00001M, "FormBounds.Y"); @@ -213,11 +219,11 @@ public static class DrawingHelperTests Assert.AreEqual(expected.ScreenBounds[i].ToRectangle(), actual.ScreenBounds[i].ToRectangle(), "ActivatedScreen.ToRectangle"); } - Assert.AreEqual(expected.ActivatedScreen.X, actual.ActivatedScreen.X, "ActivatedScreen.X"); - Assert.AreEqual(expected.ActivatedScreen.Y, actual.ActivatedScreen.Y, "ActivatedScreen.Y"); - Assert.AreEqual(expected.ActivatedScreen.Width, actual.ActivatedScreen.Width, "ActivatedScreen.Width"); - Assert.AreEqual(expected.ActivatedScreen.Height, actual.ActivatedScreen.Height, "ActivatedScreen.Height"); - Assert.AreEqual(expected.ActivatedScreen.ToRectangle(), actual.ActivatedScreen.ToRectangle(), "ActivatedScreen.ToRectangle"); + Assert.AreEqual(expected.ActivatedScreenBounds.X, actual.ActivatedScreenBounds.X, "ActivatedScreen.X"); + Assert.AreEqual(expected.ActivatedScreenBounds.Y, actual.ActivatedScreenBounds.Y, "ActivatedScreen.Y"); + Assert.AreEqual(expected.ActivatedScreenBounds.Width, actual.ActivatedScreenBounds.Width, "ActivatedScreen.Width"); + Assert.AreEqual(expected.ActivatedScreenBounds.Height, actual.ActivatedScreenBounds.Height, "ActivatedScreen.Height"); + Assert.AreEqual(expected.ActivatedScreenBounds.ToRectangle(), actual.ActivatedScreenBounds.ToRectangle(), "ActivatedScreen.ToRectangle"); } } } diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs index c080ac6979..e0267d4787 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Drawing.Models; using MouseJumpUI.Helpers; +using MouseJumpUI.Models.Drawing; namespace MouseJumpUI.UnitTests.Helpers; @@ -13,9 +13,9 @@ namespace MouseJumpUI.UnitTests.Helpers; public static class MouseHelperTests { [TestClass] - public class GetJumpLocationTests + public sealed class GetJumpLocationTests { - public class TestCase + public sealed class TestCase { public TestCase(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds, PointInfo expectedResult) { diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs rename to src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs index 722b0e786d..24d3bac4e1 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs @@ -4,17 +4,17 @@ using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Models.Drawing; -namespace MouseJumpUI.UnitTests.Drawing; +namespace MouseJumpUI.UnitTests.Models.Drawing; [TestClass] public static class RectangleInfoTests { [TestClass] - public class CenterTests + public sealed class CenterTests { - public class TestCase + public sealed class TestCase { public TestCase(RectangleInfo rectangle, PointInfo point, RectangleInfo expectedResult) { @@ -63,9 +63,9 @@ public static class RectangleInfoTests } [TestClass] - public class ClampTests + public sealed class ClampTests { - public class TestCase + public sealed class TestCase { public TestCase(RectangleInfo inner, RectangleInfo outer, RectangleInfo expectedResult) { diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs rename to src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs index 9410e66bb0..5e0bd3d4fe 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs @@ -4,16 +4,17 @@ using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Models.Drawing; namespace MouseJumpUI.UnitTests.Drawing; -public sealed class SizeInfoTests +[TestClass] +public static class SizeInfoTests { [TestClass] - public class ScaleToFitTests + public sealed class ScaleToFitTests { - public class TestCase + public sealed class TestCase { public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult) { @@ -58,9 +59,9 @@ public sealed class SizeInfoTests } [TestClass] - public class ScaleToFitRatioTests + public sealed class ScaleToFitRatioTests { - public class TestCase + public sealed class TestCase { public TestCase(SizeInfo obj, SizeInfo bounds, decimal expectedResult) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs index bc99d3a7f8..225ed68b2d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs @@ -7,95 +7,16 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; -using System.Windows.Forms; -using MouseJumpUI.Drawing.Models; -using MouseJumpUI.NativeMethods.Core; -using MouseJumpUI.NativeWrappers; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.Helpers; internal static class DrawingHelper { - public static LayoutInfo CalculateLayoutInfo( - LayoutConfig layoutConfig) - { - if (layoutConfig is null) - { - throw new ArgumentNullException(nameof(layoutConfig)); - } - - var builder = new LayoutInfo.Builder - { - LayoutConfig = layoutConfig, - }; - - builder.ActivatedScreen = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen]; - - // work out the maximum *constrained* form size - // * can't be bigger than the activated screen - // * can't be bigger than the max form size - var maxFormSize = builder.ActivatedScreen.Size - .Intersect(layoutConfig.MaximumFormSize); - - // the drawing area for screen images is inside the - // form border and inside the preview border - var maxDrawingSize = maxFormSize - .Shrink(layoutConfig.FormPadding) - .Shrink(layoutConfig.PreviewPadding); - - // scale the virtual screen to fit inside the drawing bounds - var scalingRatio = layoutConfig.VirtualScreen.Size - .ScaleToFitRatio(maxDrawingSize); - - // position the drawing bounds inside the preview border - var drawingBounds = layoutConfig.VirtualScreen.Size - .ScaleToFit(maxDrawingSize) - .PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top); - - // now we know the size of the drawing area we can work out the preview size - builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding); - - // ... and the form size - // * center the form to the activated position, but nudge it back - // inside the visible area of the activated screen if it falls outside - builder.FormBounds = builder.PreviewBounds.Size - .PlaceAt(0, 0) - .Enlarge(layoutConfig.FormPadding) - .Center(layoutConfig.ActivatedLocation) - .Clamp(builder.ActivatedScreen); - - // now calculate the positions of each of the screen images on the preview - builder.ScreenBounds = layoutConfig.ScreenBounds - .Select( - screen => screen - .Offset(layoutConfig.VirtualScreen.Location.Size.Negate()) - .Scale(scalingRatio) - .Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top)) - .ToList(); - - return builder.Build(); - } - /// - /// Resize and position the specified form. - /// - public static void PositionForm( - Form form, RectangleInfo formBounds) - { - // 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 bounds = formBounds.ToRectangle(); - form.Location = bounds.Location; - _ = form.PointToScreen(Point.Empty); - form.Size = bounds.Size; - } - - /// - /// Draw the preview background. + /// Draw the gradient-filled preview background. /// public static void DrawPreviewBackground( Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable screenBounds) @@ -127,6 +48,11 @@ internal static class DrawingHelper if (desktopHdc.IsNull) { desktopHdc = User32.GetWindowDC(desktopHwnd); + if (desktopHdc.IsNull) + { + throw new InvalidOperationException( + $"{nameof(User32.GetWindowDC)} returned null"); + } } } @@ -134,7 +60,12 @@ internal static class DrawingHelper { if (!desktopHwnd.IsNull && !desktopHdc.IsNull) { - _ = User32.ReleaseDC(desktopHwnd, desktopHdc); + var result = User32.ReleaseDC(desktopHwnd, desktopHdc); + if (result == 0) + { + throw new InvalidOperationException( + $"{nameof(User32.ReleaseDC)} returned {result}"); + } } desktopHwnd = HWND.Null; @@ -143,14 +74,20 @@ internal static class DrawingHelper /// /// Checks if the device context handle exists, and creates a new one from the - /// Graphics object if not. + /// specified Graphics object if not. /// public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc) { if (previewHdc.IsNull) { previewHdc = new HDC(previewGraphics.GetHdc()); - _ = Gdi32.SetStretchBltMode(previewHdc, MouseJumpUI.NativeMethods.Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE); + var result = Gdi32.SetStretchBltMode(previewHdc, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE); + + if (result == 0) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.SetStretchBltMode)} returned {result}"); + } } } @@ -170,7 +107,7 @@ internal static class DrawingHelper /// Draw placeholder images for any non-activated screens on the preview. /// Will release the specified device context handle if it needs to draw anything. /// - public static void DrawPreviewPlaceholders( + public static void DrawPreviewScreenPlaceholders( Graphics previewGraphics, IEnumerable screenBounds) { // we can exclude the activated screen because we've already draw @@ -183,7 +120,7 @@ internal static class DrawingHelper } /// - /// Draws screen captures from the specified desktop handle onto the target device context. + /// Draws a screen capture from the specified desktop handle onto the target device context. /// public static void DrawPreviewScreen( HDC sourceHdc, @@ -193,7 +130,7 @@ internal static class DrawingHelper { var source = sourceBounds.ToRectangle(); var target = targetBounds.ToRectangle(); - _ = Gdi32.StretchBlt( + var result = Gdi32.StretchBlt( targetHdc, target.X, target.Y, @@ -204,7 +141,12 @@ internal static class DrawingHelper source.Y, source.Width, source.Height, - MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } } /// @@ -220,7 +162,7 @@ internal static class DrawingHelper { var source = sourceBounds[i].ToRectangle(); var target = targetBounds[i].ToRectangle(); - _ = Gdi32.StretchBlt( + var result = Gdi32.StretchBlt( targetHdc, target.X, target.Y, @@ -231,7 +173,12 @@ internal static class DrawingHelper source.Y, source.Width, source.Height, - MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } } } } diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs new file mode 100644 index 0000000000..768a6f8ddd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs @@ -0,0 +1,92 @@ +// 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.Drawing; +using System.Linq; +using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; + +namespace MouseJumpUI.Helpers; + +internal static class LayoutHelper +{ + public static LayoutInfo CalculateLayoutInfo( + LayoutConfig layoutConfig) + { + if (layoutConfig is null) + { + throw new ArgumentNullException(nameof(layoutConfig)); + } + + var builder = new LayoutInfo.Builder + { + LayoutConfig = layoutConfig, + }; + + builder.ActivatedScreenBounds = layoutConfig.Screens[layoutConfig.ActivatedScreenIndex].Bounds; + + // work out the maximum *constrained* form size + // * can't be bigger than the activated screen + // * can't be bigger than the max form size + var maxFormSize = builder.ActivatedScreenBounds.Size + .Intersect(layoutConfig.MaximumFormSize); + + // the drawing area for screen images is inside the + // form border and inside the preview border + var maxDrawingSize = maxFormSize + .Shrink(layoutConfig.FormPadding) + .Shrink(layoutConfig.PreviewPadding); + + // scale the virtual screen to fit inside the drawing bounds + var scalingRatio = layoutConfig.VirtualScreenBounds.Size + .ScaleToFitRatio(maxDrawingSize); + + // position the drawing bounds inside the preview border + var drawingBounds = layoutConfig.VirtualScreenBounds.Size + .ScaleToFit(maxDrawingSize) + .PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top); + + // now we know the size of the drawing area we can work out the preview size + builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding); + + // ... and the form size + // * center the form to the activated position, but nudge it back + // inside the visible area of the activated screen if it falls outside + builder.FormBounds = builder.PreviewBounds + .Enlarge(layoutConfig.FormPadding) + .Center(layoutConfig.ActivatedLocation) + .Clamp(builder.ActivatedScreenBounds); + + // now calculate the positions of each of the screen images on the preview + builder.ScreenBounds = layoutConfig.Screens + .Select( + screen => screen.Bounds + .Offset(layoutConfig.VirtualScreenBounds.Location.ToSize().Negate()) + .Scale(scalingRatio) + .Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top)) + .ToList(); + + return builder.Build(); + } + + /// + /// Resize and position the specified form. + /// + public static void PositionForm( + Form form, RectangleInfo formBounds) + { + // 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 bounds = formBounds.ToRectangle(); + form.Location = bounds.Location; + _ = form.PointToScreen(Point.Empty); + form.Size = bounds.Size; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs index d96f2eabd9..78eb9a496d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs @@ -2,9 +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. -using System.Drawing; +using System.ComponentModel; +using System.Runtime.InteropServices; using System.Windows.Forms; -using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.Helpers; @@ -26,13 +29,33 @@ internal static class MouseHelper .Offset(desktopBounds.Location); } + /// + /// Get the current position of the cursor. + /// + 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); + } + /// /// Moves the cursor to the specified location. /// /// /// See https://github.com/mikeclayton/FancyMouse/pull/3 /// - public static void JumpCursor(PointInfo location) + public static void SetCursorPosition(PointInfo location) { // 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 @@ -51,8 +74,18 @@ internal static class MouseHelper // setting the position a second time seems to fix this and moves the // cursor to the expected location (b) var point = location.ToPoint(); - Cursor.Position = point; - Cursor.Position = point; + for (var i = 0; i < 2; i++) + { + var result = User32.SetCursorPos(point.X, point.Y); + if (!result) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + // temporary workaround for issue #1273 + MouseHelper.SimulateMouseMovementEvent(location); } /// @@ -62,26 +95,43 @@ internal static class MouseHelper /// See https://github.com/microsoft/PowerToys/issues/24523 /// https://github.com/microsoft/PowerToys/pull/24527 /// - public static void SimulateMouseMovementEvent(Point location) + public static void SimulateMouseMovementEvent(PointInfo location) { - var mouseMoveInput = new NativeMethods.INPUT + var inputs = new User32.INPUT[] { - type = NativeMethods.INPUTTYPE.INPUT_MOUSE, - data = new NativeMethods.InputUnion - { - mi = new NativeMethods.MOUSEINPUT - { - dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X), - dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y), - mouseData = 0, - dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE - | (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE, - time = 0, - dwExtraInfo = 0, - }, - }, + new( + type: User32.INPUT_TYPE.INPUT_MOUSE, + data: new User32.INPUT.DUMMYUNIONNAME( + mi: new User32.MOUSEINPUT( + dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X), + dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y), + mouseData: 0, + dwFlags: User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, + time: 0, + dwExtraInfo: ULONG_PTR.Null))), }; - var inputs = new NativeMethods.INPUT[] { mouseMoveInput }; - _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); + var result = User32.SendInput( + (uint)inputs.Length, + new User32.LPINPUT(inputs), + User32.INPUT.Size * inputs.Length); + if (result != inputs.Length) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + private static decimal CalculateAbsoluteCoordinateX(decimal x) + { + // 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(User32.SYSTEM_METRICS_INDEX.SM_CXSCREEN); + } + + internal 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(User32.SYSTEM_METRICS_INDEX.SM_CYSCREEN); } } diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs deleted file mode 100644 index bdc27b3c95..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs +++ /dev/null @@ -1,111 +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.Runtime.InteropServices; - -namespace MouseJumpUI.Helpers; - -// Win32 functions required for temporary workaround for issue #1273 -internal static class NativeMethods -{ - [DllImport("user32.dll")] - internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); - - [DllImport("user32.dll")] - internal static extern int GetSystemMetrics(SystemMetric smIndex); - - [StructLayout(LayoutKind.Sequential)] - public struct INPUT - { - internal INPUTTYPE type; - internal InputUnion data; - - internal static int Size - { - get { return Marshal.SizeOf(typeof(INPUT)); } - } - } - - [StructLayout(LayoutKind.Explicit)] - internal struct InputUnion - { - [FieldOffset(0)] - internal MOUSEINPUT mi; - [FieldOffset(0)] - internal KEYBDINPUT ki; - [FieldOffset(0)] - internal HARDWAREINPUT hi; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MOUSEINPUT - { - internal int dx; - internal int dy; - internal int mouseData; - internal uint dwFlags; - internal uint time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct KEYBDINPUT - { - internal short wVk; - internal short wScan; - internal uint dwFlags; - internal int time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HARDWAREINPUT - { - internal int uMsg; - internal short wParamL; - internal short wParamH; - } - - internal enum INPUTTYPE : uint - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } - - internal enum MOUSE_INPUT_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, - } - - internal enum SystemMetric - { - SM_CXSCREEN = 0, - SM_CYSCREEN = 1, - } - - internal static int CalculateAbsoluteCoordinateX(int x) - { - return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN); - } - - internal static int CalculateAbsoluteCoordinateY(int y) - { - return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs new file mode 100644 index 0000000000..4a61e6a6b3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs @@ -0,0 +1,94 @@ +// 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 MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.User32; + +namespace MouseJumpUI.Helpers; + +internal static class ScreenHelper +{ + /// + /// Duplicates functionality available in System.Windows.Forms.SystemInformation + /// to reduce the dependency on WinForms + /// + public 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)); + } + + public static IEnumerable GetAllScreens() + { + // enumerate the monitors attached to the system + var hMonitors = new List(); + var result = User32.EnumDisplayMonitors( + HDC.Null, + LPCRECT.Null, + (unnamedParam1, unnamedParam2, unnamedParam3, unnamedParam4) => + { + hMonitors.Add(unnamedParam1); + return true; + }, + LPARAM.Null); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.EnumDisplayMonitors)} failed with return code {result.Value}"); + } + + // get detailed info about each monitor + foreach (var hMonitor in hMonitors) + { + var monitorInfoPtr = new LPMONITORINFO( + new MONITORINFO((uint)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0)); + result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}"); + } + + var monitorInfo = monitorInfoPtr.ToStructure(); + monitorInfoPtr.Free(); + + yield return new ScreenInfo( + handle: hMonitor, + primary: monitorInfo.dwFlags.HasFlag(User32.MONITOR_INFO_FLAGS.MONITORINFOF_PRIMARY), + displayArea: new RectangleInfo( + monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.top, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top), + workingArea: new RectangleInfo( + monitorInfo.rcWork.left, + monitorInfo.rcWork.top, + monitorInfo.rcWork.right - monitorInfo.rcWork.left, + monitorInfo.rcWork.bottom - monitorInfo.rcWork.top)); + } + } + + public static HMONITOR MonitorFromPoint( + PointInfo pt) + { + var hMonitor = User32.MonitorFromPoint( + new((int)pt.X, (int)pt.Y), + User32.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + if (hMonitor.IsNull) + { + throw new InvalidOperationException($"no monitor found for point {pt}"); + } + + return hMonitor; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs index b068989062..7dbc1c6dd4 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -9,20 +9,28 @@ using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; using ManagedCommon; -using MouseJumpUI.Drawing.Models; +using Microsoft.PowerToys.Settings.UI.Library; using MouseJumpUI.Helpers; -using MouseJumpUI.NativeMethods.Core; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI; internal partial class MainForm : Form { - public MainForm() + public MainForm(MouseJumpSettings settings) { this.InitializeComponent(); + this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); this.ShowThumbnail(); } + public MouseJumpSettings Settings + { + get; + } + private void MainForm_Load(object sender, EventArgs e) { } @@ -32,6 +40,65 @@ internal partial class MainForm : Form if (e.KeyCode == Keys.Escape) { this.OnDeactivate(EventArgs.Empty); + return; + } + + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + + var currentLocation = MouseHelper.GetCursorPosition(); + var currentScreenHandle = ScreenHelper.MonitorFromPoint(currentLocation); + var currentScreen = screens + .Single(item => item.Screen.Handle == currentScreenHandle.Value); + var targetScreenNumber = default(int?); + + if (((e.KeyCode >= Keys.D1) && (e.KeyCode <= Keys.D9)) + || ((e.KeyCode >= Keys.NumPad1) && (e.KeyCode <= Keys.NumPad9))) + { + // number keys 1-9 or numpad keys 1-9 - move to the numbered screen + var screenNumber = e.KeyCode - Keys.D0; + if (screenNumber <= screens.Count) + { + targetScreenNumber = screenNumber; + } + } + else if (e.KeyCode == Keys.P) + { + // "P" - move to the primary screen + targetScreenNumber = screens.Single(item => item.Screen.Primary).Number; + } + else if (e.KeyCode == Keys.Left) + { + // move to the previous screen + targetScreenNumber = currentScreen.Number == 1 + ? screens.Count + : currentScreen.Number - 1; + } + else if (e.KeyCode == Keys.Right) + { + // move to the next screen + targetScreenNumber = currentScreen.Number == screens.Count + ? 1 + : currentScreen.Number + 1; + } + else if (e.KeyCode == Keys.Home) + { + // move to the first screen + targetScreenNumber = 1; + } + else if (e.KeyCode == Keys.End) + { + // move to the last screen + targetScreenNumber = screens.Count; + } + + if (targetScreenNumber.HasValue) + { + MouseHelper.SetCursorPosition( + screens[targetScreenNumber.Value - 1].Screen.Bounds.Midpoint); + this.OnDeactivate(EventArgs.Empty); } } @@ -59,15 +126,13 @@ internal partial class MainForm : Form if (mouseEventArgs.Button == MouseButtons.Left) { // plain click - move mouse pointer + var virtualScreen = ScreenHelper.GetVirtualScreen(); var scaledLocation = MouseHelper.GetJumpLocation( new PointInfo(mouseEventArgs.X, mouseEventArgs.Y), new SizeInfo(this.Thumbnail.Size), - new RectangleInfo(SystemInformation.VirtualScreen)); + virtualScreen); Logger.LogInfo($"scaled location = {scaledLocation}"); - MouseHelper.JumpCursor(scaledLocation); - - // Simulate mouse input for handlers that won't just catch the Cursor change - MouseHelper.SimulateMouseMovementEvent(scaledLocation.ToPoint()); + MouseHelper.SetCursorPosition(scaledLocation); Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); } @@ -76,57 +141,93 @@ internal partial class MainForm : Form public void ShowThumbnail() { - var screens = Screen.AllScreens; - foreach (var i in Enumerable.Range(0, screens.Length)) + var stopwatch = Stopwatch.StartNew(); + var layoutInfo = MainForm.GetLayoutInfo(this); + LayoutHelper.PositionForm(this, layoutInfo.FormBounds); + MainForm.RenderPreview(this, layoutInfo); + stopwatch.Stop(); + + // we have to activate the form to make sure the deactivate event fires + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); + this.Activate(); + } + + private static LayoutInfo GetLayoutInfo(MainForm form) + { + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + foreach (var screen in screens) { - var screen = screens[i]; Logger.LogInfo(string.Join( '\n', - $"screen[{i}] = \"{screen.DeviceName}\"", - $"\tprimary = {screen.Primary}", - $"\tbounds = {screen.Bounds}", - $"\tworking area = {screen.WorkingArea}")); + $"screen[{screen.Number}]", + $"\tprimary = {screen.Screen.Primary}", + $"\tdisplay area = {screen.Screen.DisplayArea}", + $"\tworking area = {screen.Screen.WorkingArea}")); } // collect together some values that we need for calculating layout - var activatedLocation = Cursor.Position; + var activatedLocation = MouseHelper.GetCursorPosition(); + var activatedScreenHandle = ScreenHelper.MonitorFromPoint(activatedLocation); + var activatedScreenIndex = screens + .Single(item => item.Screen.Handle == activatedScreenHandle.Value) + .Index; + var layoutConfig = new LayoutConfig( - virtualScreen: SystemInformation.VirtualScreen, - screenBounds: Screen.AllScreens.Select(screen => screen.Bounds), + virtualScreenBounds: ScreenHelper.GetVirtualScreen(), + screens: screens.Select(item => item.Screen).ToList(), activatedLocation: activatedLocation, - activatedScreen: Array.IndexOf(Screen.AllScreens, Screen.FromPoint(activatedLocation)), - maximumFormSize: new Size(1600, 1200), - formPadding: this.panel1.Padding, - previewPadding: new Padding(0)); + activatedScreenIndex: activatedScreenIndex, + activatedScreenNumber: activatedScreenIndex + 1, + maximumFormSize: new( + form.Settings.Properties.ThumbnailSize.Width, + form.Settings.Properties.ThumbnailSize.Height), + formPadding: new( + form.panel1.Padding.Left, + form.panel1.Padding.Top, + form.panel1.Padding.Right, + form.panel1.Padding.Bottom), + previewPadding: new(0)); Logger.LogInfo(string.Join( '\n', $"Layout config", $"-------------", - $"virtual screen = {layoutConfig.VirtualScreen}", - $"activated location = {layoutConfig.ActivatedLocation}", - $"activated screen = {layoutConfig.ActivatedScreen}", - $"maximum form size = {layoutConfig.MaximumFormSize}", - $"form padding = {layoutConfig.FormPadding}", - $"preview padding = {layoutConfig.PreviewPadding}")); + $"virtual screen = {layoutConfig.VirtualScreenBounds}", + $"activated location = {layoutConfig.ActivatedLocation}", + $"activated screen index = {layoutConfig.ActivatedScreenIndex}", + $"activated screen number = {layoutConfig.ActivatedScreenNumber}", + $"maximum form size = {layoutConfig.MaximumFormSize}", + $"form padding = {layoutConfig.FormPadding}", + $"preview padding = {layoutConfig.PreviewPadding}")); // calculate the layout coordinates for everything - var layoutInfo = DrawingHelper.CalculateLayoutInfo(layoutConfig); + var layoutInfo = LayoutHelper.CalculateLayoutInfo(layoutConfig); Logger.LogInfo(string.Join( '\n', $"Layout info", $"-----------", $"form bounds = {layoutInfo.FormBounds}", $"preview bounds = {layoutInfo.PreviewBounds}", - $"activated screen = {layoutInfo.ActivatedScreen}")); + $"activated screen = {layoutInfo.ActivatedScreenBounds}")); - DrawingHelper.PositionForm(this, layoutInfo.FormBounds); + return layoutInfo; + } + + private static void RenderPreview( + MainForm form, LayoutInfo layoutInfo) + { + var layoutConfig = layoutInfo.LayoutConfig; + + var stopwatch = Stopwatch.StartNew(); // initialize the preview image var preview = new Bitmap( (int)layoutInfo.PreviewBounds.Width, (int)layoutInfo.PreviewBounds.Height, PixelFormat.Format32bppArgb); - this.Thumbnail.Image = preview; + form.Thumbnail.Image = preview; using var previewGraphics = Graphics.FromImage(preview); @@ -137,49 +238,51 @@ internal partial class MainForm : Form var previewHdc = HDC.Null; try { + // sort the source and target screen areas, 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 = layoutConfig.Screens + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutConfig.Screens.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .Select(screen => screen.Bounds) + .ToList(); + var targetScreens = layoutInfo.ScreenBounds + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .ToList(); + DrawingHelper.EnsureDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); - - // we have to capture the screen where we're going to show the form first - // as the form will obscure the screen as soon as it's visible - var activatedStopwatch = Stopwatch.StartNew(); DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewScreen( - desktopHdc, - previewHdc, - layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen], - layoutInfo.ScreenBounds[layoutConfig.ActivatedScreen]); - activatedStopwatch.Stop(); - // show the placeholder images if it looks like it might take a while - // to capture the remaining screenshot images - if (activatedStopwatch.ElapsedMilliseconds > 250) + var placeholdersDrawn = false; + for (var i = 0; i < sourceScreens.Count; i++) { - var activatedArea = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen].Area; - var totalArea = layoutConfig.ScreenBounds.Sum(screen => screen.Area); - if ((activatedArea / totalArea) < 0.5M) + DrawingHelper.DrawPreviewScreen( + desktopHdc, previewHdc, sourceScreens[i], targetScreens[i]); + + // show the placeholder images and show the form if it looks like it might take + // a while to capture the remaining screenshot images (but only if there are any) + if ((i < (sourceScreens.Count - 1)) && (stopwatch.ElapsedMilliseconds > 250)) { - // we need to release the device context handle before we can draw the placeholders + // we need to release the device context handle before we draw the placeholders // using the Graphics object otherwise we'll get an error from GDI saying // "Object is currently in use elsewhere" DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewPlaceholders( - previewGraphics, - layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen)); - MainForm.ShowPreview(this); - } - } - // draw the remaining screen captures (if any) on the preview image - var sourceScreens = layoutConfig.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList(); - if (sourceScreens.Any()) - { - DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewScreens( - desktopHdc, - previewHdc, - sourceScreens, - layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList()); - MainForm.ShowPreview(this); + if (!placeholdersDrawn) + { + // draw placeholders for any undrawn screens + DrawingHelper.DrawPreviewScreenPlaceholders( + previewGraphics, + targetScreens.Where((_, idx) => idx > i)); + placeholdersDrawn = true; + } + + MainForm.RefreshPreview(form); + + // we've still got more screens to draw so open the device context again + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + } } } finally @@ -188,13 +291,11 @@ internal partial class MainForm : Form DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); } - // we have to activate the form to make sure the deactivate event fires - MainForm.ShowPreview(this); - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); - this.Activate(); + MainForm.RefreshPreview(form); + stopwatch.Stop(); } - private static void ShowPreview(MainForm form) + private static void RefreshPreview(MainForm form) { if (!form.Visible) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs similarity index 88% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs index 2d7a5791b9..9437bf0531 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs @@ -4,7 +4,7 @@ using System.Windows.Forms; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods. @@ -16,11 +16,6 @@ public sealed class PaddingInfo { } - public PaddingInfo(Padding padding) - : this(padding.Left, padding.Top, padding.Right, padding.Bottom) - { - } - public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom) { this.Left = left; diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs index 1e8481df4c..40e452a378 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs @@ -4,7 +4,7 @@ using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Point object with some extra utility methods. @@ -32,7 +32,10 @@ public sealed class PointInfo get; } - public SizeInfo Size => new((int)this.X, (int)this.Y); + public SizeInfo ToSize() + { + return new((int)this.X, (int)this.Y); + } public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor); diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs similarity index 86% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs index ebfaeb0b9e..af46b85470 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. @@ -69,6 +69,14 @@ public sealed class RectangleInfo public decimal Area => 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 bool Contains(RectangleInfo rect) => + (this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) && + (this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height); + public RectangleInfo Enlarge(PaddingInfo padding) => new( this.X + padding.Left, this.Y + padding.Top, diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs similarity index 98% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs index 4167c89df5..a91febc23c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Size object with some extra utility methods. diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs similarity index 51% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs index bc80d991d4..540a980a7c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing; using System.Linq; -using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Layout; /// /// Represents a collection of values needed for calculating the MainForm layout. @@ -17,29 +17,31 @@ namespace MouseJumpUI.Drawing.Models; public sealed class LayoutConfig { public LayoutConfig( - Rectangle virtualScreen, - IEnumerable screenBounds, - Point activatedLocation, - int activatedScreen, - Size maximumFormSize, - Padding formPadding, - Padding previewPadding) + RectangleInfo virtualScreenBounds, + List screens, + PointInfo activatedLocation, + int activatedScreenIndex, + int activatedScreenNumber, + SizeInfo maximumFormSize, + PaddingInfo formPadding, + PaddingInfo previewPadding) { // make sure the virtual screen entirely contains all of the individual screen bounds - ArgumentNullException.ThrowIfNull(screenBounds); - if (screenBounds.Any(screen => !virtualScreen.Contains(screen))) + ArgumentNullException.ThrowIfNull(virtualScreenBounds); + ArgumentNullException.ThrowIfNull(screens); + if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds))) { - throw new ArgumentException($"'{nameof(virtualScreen)}' must contain all of the screens in '{nameof(screenBounds)}'", nameof(virtualScreen)); + throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds)); } - this.VirtualScreen = new RectangleInfo(virtualScreen); - this.ScreenBounds = new( - screenBounds.Select(screen => new RectangleInfo(screen)).ToList()); - this.ActivatedLocation = new(activatedLocation); - this.ActivatedScreen = activatedScreen; - this.MaximumFormSize = new(maximumFormSize); - this.FormPadding = new(formPadding); - this.PreviewPadding = new(previewPadding); + this.VirtualScreenBounds = virtualScreenBounds; + this.Screens = new(screens.ToList()); + this.ActivatedLocation = activatedLocation; + this.ActivatedScreenIndex = activatedScreenIndex; + this.ActivatedScreenNumber = activatedScreenNumber; + this.MaximumFormSize = maximumFormSize; + this.FormPadding = formPadding; + this.PreviewPadding = previewPadding; } /// @@ -49,15 +51,15 @@ public sealed class LayoutConfig /// The Virtual Screen is the bounding rectangle of all the monitors. /// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen /// - public RectangleInfo VirtualScreen + public RectangleInfo VirtualScreenBounds { get; } /// - /// Gets the bounds of all of the screens connected to the system. + /// Gets a collection containing the individual screens connected to the system. /// - public ReadOnlyCollection ScreenBounds + public ReadOnlyCollection Screens { get; } @@ -67,8 +69,8 @@ public sealed class LayoutConfig /// /// /// The preview form will be centered on this location unless there are any - /// constraints such as the being too close to edge of a screen, in which case - /// the form will be displayed as close as possible to this location. + /// constraints such as being too close to edge of a screen, in which case + /// the form will be displayed centered as close as possible to this location. /// public PointInfo ActivatedLocation { @@ -77,8 +79,19 @@ public sealed class LayoutConfig /// /// Gets the index of the screen the cursor was on when the form was activated. + /// The value is an index into the ScreenBounds array and is 0-indexed as a result. /// - public int ActivatedScreen + public int ActivatedScreenIndex + { + get; + } + + /// + /// Gets the screen number the cursor was on when the form was activated. + /// The value matches the screen numbering scheme in the "Display Settings" dialog + /// and is 1-indexed as a result. + /// + public int ActivatedScreenNumber { get; } diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs similarity index 85% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs index 30eeed17c6..8a2cf74154 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs @@ -6,8 +6,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using MouseJumpUI.Models.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Layout; public sealed class LayoutInfo { @@ -42,7 +43,7 @@ public sealed class LayoutInfo set; } - public RectangleInfo? ActivatedScreen + public RectangleInfo? ActivatedScreenBounds { get; set; @@ -55,7 +56,7 @@ public sealed class LayoutInfo formBounds: this.FormBounds ?? throw new InvalidOperationException(), previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(), screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(), - activatedScreen: this.ActivatedScreen ?? throw new InvalidOperationException()); + activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException()); } } @@ -64,7 +65,7 @@ public sealed class LayoutInfo RectangleInfo formBounds, RectangleInfo previewBounds, IEnumerable screenBounds, - RectangleInfo activatedScreen) + RectangleInfo activatedScreenBounds) { this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); @@ -72,7 +73,7 @@ public sealed class LayoutInfo this.ScreenBounds = new( (screenBounds ?? throw new ArgumentNullException(nameof(screenBounds))) .ToList()); - this.ActivatedScreen = activatedScreen ?? throw new ArgumentNullException(nameof(activatedScreen)); + this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds)); } /// @@ -104,7 +105,7 @@ public sealed class LayoutInfo get; } - public RectangleInfo ActivatedScreen + public RectangleInfo ActivatedScreenBounds { get; } diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs new file mode 100644 index 0000000000..d7791fb063 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs @@ -0,0 +1,46 @@ +// 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 MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.Models.Screen; + +/// +/// 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 +{ + internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) + { + this.Handle = handle; + this.Primary = primary; + this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea)); + this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea)); + } + + public int Handle + { + get; + } + + public bool Primary + { + get; + } + + public RectangleInfo DisplayArea + { + get; + } + + public RectangleInfo Bounds => + this.DisplayArea; + + public RectangleInfo WorkingArea + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj index 1300f083ab..8d15866ace 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj +++ b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj @@ -63,5 +63,6 @@ + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs index 5c2c500820..851cb77a46 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs @@ -2,35 +2,43 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// A Boolean variable (should be TRUE or FALSE). -/// This type is declared in WinDef.h as follows: -/// typedef int BOOL; -/// -/// -/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types -/// -internal readonly struct BOOL +internal static partial class Core { - public readonly int Value; - - public BOOL(int value) + /// + /// A Boolean variable (should be TRUE or FALSE). + /// This type is declared in WinDef.h as follows: + /// typedef int BOOL; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct BOOL { - this.Value = value; + public readonly int Value; + + public BOOL(int value) + { + this.Value = value; + } + + public BOOL(bool value) + { + this.Value = value ? 1 : 0; + } + + public static implicit operator bool(BOOL value) => value.Value != 0; + + public static implicit operator BOOL(bool value) => new(value); + + public static implicit operator int(BOOL value) => value.Value; + + public static implicit operator BOOL(int value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } } - - public BOOL(bool value) - { - this.Value = value ? 1 : 0; - } - - public static implicit operator bool(BOOL value) => value.Value != 0; - - public static implicit operator BOOL(bool value) => new(value); - - public static implicit operator int(BOOL value) => value.Value; - - public static implicit operator BOOL(int value) => new(value); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs new file mode 100644 index 0000000000..a44da78727 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs @@ -0,0 +1,45 @@ +// 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 MouseJumpUI.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( + 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/MouseJumpUI/NativeMethods/Core/DWORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs new file mode 100644 index 0000000000..9009b63c86 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs @@ -0,0 +1,35 @@ +// 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 MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 32-bit unsigned integer. The range is 0 through 4294967295 decimal. + /// This type is declared in IntSafe.h as follows: + /// typedef unsigned long DWORD; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct DWORD + { + public readonly uint Value; + + public DWORD(uint value) + { + this.Value = value; + } + + public static implicit operator uint(DWORD value) => value.Value; + + public static implicit operator DWORD(uint value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs new file mode 100644 index 0000000000..1df2624ed5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs @@ -0,0 +1,41 @@ +// 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; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to an object. + /// This type is declared in WinNT.h as follows: + /// typedef PVOID HANDLE; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HANDLE + { + public static readonly HANDLE Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HANDLE(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HANDLE.Null.Value; + + public static implicit operator IntPtr(HANDLE value) => value.Value; + + public static implicit operator HANDLE(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs index 4ebfce933b..b02a811c06 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs @@ -4,26 +4,34 @@ using System; -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// 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 +internal static partial class Core { - public static readonly HDC Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HDC(IntPtr value) + /// + /// 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 { - this.Value = value; - } + public static readonly HDC Null = new(IntPtr.Zero); - public bool IsNull => this.Value == HDC.Null.Value; + public readonly IntPtr Value; + + public HDC(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HDC.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs new file mode 100644 index 0000000000..047c654f6f --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs @@ -0,0 +1,49 @@ +// 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; + +namespace MouseJumpUI.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 implicit operator HMONITOR(int value) => new(value); + + public static implicit operator IntPtr(HMONITOR value) => value.Value; + + public static implicit operator HMONITOR(IntPtr value) => new(value); + + public static implicit operator HANDLE(HMONITOR value) => new(value.Value); + + public static implicit operator HMONITOR(HANDLE value) => new(value.Value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs index 2478e0c8bf..e12f2846f3 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs @@ -4,26 +4,34 @@ using System; -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// A handle to a window. -/// This type is declared in WinDef.h as follows: -/// typedef HANDLE HWND; -/// -/// -/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types -/// -internal readonly struct HWND +internal static partial class Core { - public static readonly HWND Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HWND(IntPtr value) + /// + /// A handle to a window. + /// This type is declared in WinDef.h as follows: + /// typedef HANDLE HWND; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HWND { - this.Value = value; - } + public static readonly HWND Null = new(IntPtr.Zero); - public bool IsNull => this.Value == HWND.Null.Value; + public readonly IntPtr Value; + + public HWND(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HWND.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs new file mode 100644 index 0000000000..2d2dd82283 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs @@ -0,0 +1,35 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/Core/LPARAM.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs new file mode 100644 index 0000000000..6a227f5e21 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.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; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A message parameter. + /// This type is declared in WinDef.h as follows: + /// typedef LONG_PTR LPARAM; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LPARAM + { + public static readonly LPARAM Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPARAM(IntPtr value) + { + this.Value = value; + } + + public static implicit operator IntPtr(LPARAM value) => value.Value; + + public static implicit operator LPARAM(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs new file mode 100644 index 0000000000..d19abd9c39 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs @@ -0,0 +1,49 @@ +// 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 MouseJumpUI.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); + } + + 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 implicit operator LPCRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs new file mode 100644 index 0000000000..db3423decd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.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; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.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); + } + + 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 implicit operator LPPOINT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs new file mode 100644 index 0000000000..087e22abe3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs @@ -0,0 +1,48 @@ +// 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 MouseJumpUI.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); + } + + 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 implicit operator LPRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs new file mode 100644 index 0000000000..d4513d6740 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.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.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.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( + 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/MouseJumpUI/NativeMethods/Core/RECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs new file mode 100644 index 0000000000..ff107f6ad8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs @@ -0,0 +1,45 @@ +// 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 MouseJumpUI.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( + 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/MouseJumpUI/NativeMethods/Core/UINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs new file mode 100644 index 0000000000..e135212ec9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.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. +namespace MouseJumpUI.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 override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs new file mode 100644 index 0000000000..76d99d88be --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs @@ -0,0 +1,44 @@ +// 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; + +namespace MouseJumpUI.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 implicit operator ULONG_PTR(UIntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs new file mode 100644 index 0000000000..642d0b0514 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs @@ -0,0 +1,35 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs index f91c6e05f8..994eeb7165 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs @@ -14,7 +14,7 @@ internal static partial class Gdi32 /// /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt /// - public enum ROP_CODE : uint + internal enum ROP_CODE : uint { BLACKNESS = 0x00000042, CAPTUREBLT = 0x40000000, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs index 71214b2204..575e092982 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs @@ -12,7 +12,7 @@ internal static partial class Gdi32 /// /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode /// - public enum STRETCH_BLT_MODE : int + internal enum STRETCH_BLT_MODE : int { BLACKONWHITE = 1, COLORONCOLOR = 3, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs similarity index 90% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs index 085d03295f..410af308d4 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -21,7 +21,7 @@ internal static partial class Gdi32 /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode /// [LibraryImport(Libraries.Gdi32)] - public static partial int SetStretchBltMode( + internal static partial int SetStretchBltMode( HDC hdc, STRETCH_BLT_MODE mode); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs index 2e7524358e..345391d666 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -23,7 +23,7 @@ internal static partial class Gdi32 /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt /// [LibraryImport(Libraries.Gdi32)] - public static partial BOOL StretchBlt( + internal static partial BOOL StretchBlt( HDC hdcDest, int xDest, int yDest, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs new file mode 100644 index 0000000000..6d5a0687bb --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs @@ -0,0 +1,33 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs new file mode 100644 index 0000000000..ad55845cf9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs @@ -0,0 +1,26 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The GetMonitorInfo function retrieves information about a display monitor. + /// + /// + /// 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 GetMonitorInfoW( + HMONITOR hMonitor, + LPMONITORINFO lpmi); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs index 4263aa0b7d..ea0b79db9b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -26,6 +26,6 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc /// [LibraryImport(Libraries.User32)] - public static partial HDC GetWindowDC( + internal static partial HDC GetWindowDC( HWND hWnd); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs new file mode 100644 index 0000000000..f44a2c7972 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs @@ -0,0 +1,53 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs new file mode 100644 index 0000000000..0e366c8163 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs @@ -0,0 +1,22 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs new file mode 100644 index 0000000000..24fb270938 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs @@ -0,0 +1,39 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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(INPUT)); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs new file mode 100644 index 0000000000..5d5fc3b035 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs @@ -0,0 +1,24 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs new file mode 100644 index 0000000000..f20e6a2f58 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs @@ -0,0 +1,22 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs new file mode 100644 index 0000000000..93878544e5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs @@ -0,0 +1,26 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs similarity index 92% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs index f745c13c27..a6f6caa355 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -22,7 +22,7 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc /// [LibraryImport(Libraries.User32)] - public static partial int ReleaseDC( + internal static partial int ReleaseDC( HWND hWnd, HDC hDC); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs new file mode 100644 index 0000000000..254b60eecd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.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.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs new file mode 100644 index 0000000000..1a4bcf8546 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs @@ -0,0 +1,51 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs new file mode 100644 index 0000000000..89e8f60218 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs @@ -0,0 +1,24 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs new file mode 100644 index 0000000000..a5c9bc782b --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs @@ -0,0 +1,43 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs new file mode 100644 index 0000000000..ff689a5931 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs @@ -0,0 +1,72 @@ +// 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.Runtime.InteropServices; + +namespace MouseJumpUI.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, true); + 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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs new file mode 100644 index 0000000000..80f9bbe2fc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs @@ -0,0 +1,46 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs new file mode 100644 index 0000000000..b6f3cba549 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.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; +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs new file mode 100644 index 0000000000..b7bfba63bf --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs @@ -0,0 +1,28 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs new file mode 100644 index 0000000000..f6cbfa75bc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs @@ -0,0 +1,25 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs similarity index 88% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs index bb5ed16514..4d16b95e15 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -20,5 +20,5 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow /// [LibraryImport(Libraries.User32)] - public static partial HWND GetDesktopWindow(); + internal static partial HWND GetDesktopWindow(); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs new file mode 100644 index 0000000000..7791c22963 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs @@ -0,0 +1,26 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs new file mode 100644 index 0000000000..b2ca6b3b68 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs @@ -0,0 +1,110 @@ +// 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 MouseJumpUI.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/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs new file mode 100644 index 0000000000..b548148925 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs @@ -0,0 +1,28 @@ +// 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 MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.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/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs deleted file mode 100644 index 7613b8825c..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs +++ /dev/null @@ -1,21 +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 MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class Gdi32 -{ - public static int SetStretchBltMode(HDC hdc, NativeMethods.Gdi32.STRETCH_BLT_MODE mode) - { - var result = NativeMethods.Gdi32.SetStretchBltMode(hdc, mode); - - return result == 0 - ? throw new InvalidOperationException( - $"{nameof(Gdi32.SetStretchBltMode)} returned {result}") - : result; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs deleted file mode 100644 index 115edafee9..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs +++ /dev/null @@ -1,43 +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 MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class Gdi32 -{ - public static BOOL StretchBlt( - HDC hdcDest, - int xDest, - int yDest, - int wDest, - int hDest, - HDC hdcSrc, - int xSrc, - int ySrc, - int wSrc, - int hSrc, - NativeMethods.Gdi32.ROP_CODE rop) - { - var result = NativeMethods.Gdi32.StretchBlt( - hdcDest, - xDest, - yDest, - wDest, - hDest, - hdcSrc, - xSrc, - ySrc, - wSrc, - hSrc, - rop); - - return result - ? result - : throw new InvalidOperationException( - $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs deleted file mode 100644 index 8df89f7e39..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs +++ /dev/null @@ -1,16 +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 MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static HWND GetDesktopWindow() - { - return NativeMethods.User32.GetDesktopWindow(); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs deleted file mode 100644 index 6379e3f73a..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs +++ /dev/null @@ -1,21 +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 MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static HDC GetWindowDC(HWND hWnd) - { - var hdc = NativeMethods.User32.GetWindowDC(hWnd); - - return hdc.IsNull - ? throw new InvalidOperationException( - $"{nameof(User32.GetWindowDC)} returned null") - : hdc; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs deleted file mode 100644 index 44a8318b85..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs +++ /dev/null @@ -1,21 +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 MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static int ReleaseDC(HWND hWnd, HDC hDC) - { - var result = NativeMethods.User32.ReleaseDC(hWnd, hDC); - - return result == 0 - ? throw new InvalidOperationException( - $"{nameof(User32.ReleaseDC)} returned {result}") - : result; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Program.cs b/src/modules/MouseUtils/MouseJumpUI/Program.cs index f5932c13c0..7d3c385730 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Program.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Program.cs @@ -3,15 +3,18 @@ // See the LICENSE file in the project root for more information. using System; +using System.IO; +using System.Text.Json; using System.Windows.Forms; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; namespace MouseJumpUI; internal static class Program { /// - /// The main entry point for the application. + /// The main entry point for the application. /// [STAThread] private static void Main() @@ -35,6 +38,34 @@ internal static class Program return; } - Application.Run(new MainForm()); + var settings = Program.ReadSettings(); + var mainForm = new MainForm(settings); + + Application.Run(mainForm); + } + + private static MouseJumpSettings ReadSettings() + { + var settingsUtils = new SettingsUtils(); + var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName); + if (!File.Exists(settingsPath)) + { + var scaffoldSettings = new MouseJumpSettings(); + settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName); + } + + var settings = new MouseJumpSettings(); + try + { + settings = settingsUtils.GetSettings(MouseJumpSettings.ModuleName); + } + catch (Exception ex) + { + var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}"; + Logger.LogInfo(errorMessage); + Logger.LogDebug(errorMessage); + } + + return settings; } } diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs index 1d5a9ad480..524039b06e 100644 --- a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs @@ -11,9 +11,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("activation_shortcut")] public HotkeySettings ActivationShortcut { get; set; } + [JsonPropertyName("thumbnail_size")] + public MouseJumpThumbnailSize ThumbnailSize { get; set; } + public MouseJumpProperties() { ActivationShortcut = new HotkeySettings(true, false, false, true, 0x44); + ThumbnailSize = new MouseJumpThumbnailSize(); } } } diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs new file mode 100644 index 0000000000..58af7d3683 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs @@ -0,0 +1,68 @@ +// 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.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class MouseJumpThumbnailSize : INotifyPropertyChanged + { + private int _width; + private int _height; + + [JsonPropertyName("width")] + public int Width + { + get + { + return _width; + } + + set + { + var newWidth = Math.Max(0, value); + if (newWidth != _width) + { + _width = newWidth; + OnPropertyChanged(); + } + } + } + + [JsonPropertyName("height")] + public int Height + { + get + { + return _height; + } + + set + { + var newHeight = Math.Max(0, value); + if (newHeight != _height) + { + _height = newHeight; + OnPropertyChanged(); + } + } + } + + public MouseJumpThumbnailSize() + { + Width = 1600; + Height = 1200; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 25761aee7c..5eb65309d4 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -3158,4 +3158,21 @@ Activate by holding the key for the character you want to add an accent to, then Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning. + + Thumbnail Size + + + Constrain thumbnail image size to a maximum of + + + pixels + + + Maximum height (px) + px = pixels + + + Maximum width (px) + px = pixels + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index 91c866449c..e781e92a7e 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; using System.Runtime.CompilerServices; using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; @@ -86,6 +87,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig; + MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += MouseJumpThumbnailSizePropertyChanged; if (mousePointerCrosshairsSettingsRepository == null) { @@ -599,6 +601,29 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public MouseJumpThumbnailSize MouseJumpThumbnailSize + { + get + { + return MouseJumpSettingsConfig.Properties.ThumbnailSize; + } + + set + { + if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width) + && (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height)) + { + MouseJumpSettingsConfig.Properties.ThumbnailSize = value; + NotifyMouseJumpPropertyChanged(); + } + } + } + + public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e) + { + NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize)); + } + public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null) { OnPropertyChanged(propertyName); diff --git a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml index 09c60be62e..054b31b59a 100644 --- a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml +++ b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml @@ -229,6 +229,81 @@ MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind Path=ViewModel.MouseJumpActivationShortcut, Mode=TwoWay}" /> + + + + + + + + + + + + + +