mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[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)
This commit is contained in:
1
.github/actions/spell-check/excludes.txt
vendored
1
.github/actions/spell-check/excludes.txt
vendored
@@ -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$
|
||||
|
||||
11
.github/actions/spell-check/expect.txt
vendored
11
.github/actions/spell-check/expect.txt
vendored
@@ -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
|
||||
|
||||
@@ -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<Rectangle>
|
||||
virtualScreenBounds: new(0, 0, 5120, 1440),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
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<Rectangle>
|
||||
virtualScreenBounds: new(-1920, -472, 7040, 1912),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
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<Rectangle>
|
||||
virtualScreenBounds: new(0, 0, 7168, 1440),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
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<Rectangle>
|
||||
virtualScreenBounds: new(0, 0, 7424, 1440),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize and position the specified form.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the preview background.
|
||||
/// Draw the gradient-filled preview background.
|
||||
/// </summary>
|
||||
public static void DrawPreviewBackground(
|
||||
Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable<RectangleInfo> 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
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the device context handle exists, and creates a new one from the
|
||||
/// Graphics object if not.
|
||||
/// specified Graphics object if not.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
public static void DrawPreviewPlaceholders(
|
||||
public static void DrawPreviewScreenPlaceholders(
|
||||
Graphics previewGraphics, IEnumerable<RectangleInfo> screenBounds)
|
||||
{
|
||||
// we can exclude the activated screen because we've already draw
|
||||
@@ -183,7 +120,7 @@ internal static class DrawingHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
92
src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs
Normal file
92
src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize and position the specified form.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current position of the cursor.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the cursor to the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://github.com/mikeclayton/FancyMouse/pull/3
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,26 +95,43 @@ internal static class MouseHelper
|
||||
/// See https://github.com/microsoft/PowerToys/issues/24523
|
||||
/// https://github.com/microsoft/PowerToys/pull/24527
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
94
src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs
Normal file
94
src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Duplicates functionality available in System.Windows.Forms.SystemInformation
|
||||
/// to reduce the dependency on WinForms
|
||||
/// </summary>
|
||||
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<ScreenInfo> GetAllScreens()
|
||||
{
|
||||
// enumerate the monitors attached to the system
|
||||
var hMonitors = new List<HMONITOR>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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}",
|
||||
$"virtual screen = {layoutConfig.VirtualScreenBounds}",
|
||||
$"activated location = {layoutConfig.ActivatedLocation}",
|
||||
$"activated screen = {layoutConfig.ActivatedScreen}",
|
||||
$"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(
|
||||
|
||||
if (!placeholdersDrawn)
|
||||
{
|
||||
// draw placeholders for any undrawn screens
|
||||
DrawingHelper.DrawPreviewScreenPlaceholders(
|
||||
previewGraphics,
|
||||
layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen));
|
||||
MainForm.ShowPreview(this);
|
||||
}
|
||||
targetScreens.Where((_, idx) => idx > i));
|
||||
placeholdersDrawn = true;
|
||||
}
|
||||
|
||||
// draw the remaining screen captures (if any) on the preview image
|
||||
var sourceScreens = layoutConfig.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList();
|
||||
if (sourceScreens.Any())
|
||||
{
|
||||
MainForm.RefreshPreview(form);
|
||||
|
||||
// we've still got more screens to draw so open the device context again
|
||||
DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc);
|
||||
DrawingHelper.DrawPreviewScreens(
|
||||
desktopHdc,
|
||||
previewHdc,
|
||||
sourceScreens,
|
||||
layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList());
|
||||
MainForm.ShowPreview(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MouseJumpUI.Drawing.Models;
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Drawing.Models;
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Drawing.Models;
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
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,
|
||||
@@ -5,7 +5,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Drawing.Models;
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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<Rectangle> screenBounds,
|
||||
Point activatedLocation,
|
||||
int activatedScreen,
|
||||
Size maximumFormSize,
|
||||
Padding formPadding,
|
||||
Padding previewPadding)
|
||||
RectangleInfo virtualScreenBounds,
|
||||
List<ScreenInfo> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
public RectangleInfo VirtualScreen
|
||||
public RectangleInfo VirtualScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds of all of the screens connected to the system.
|
||||
/// Gets a collection containing the individual screens connected to the system.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<RectangleInfo> ScreenBounds
|
||||
public ReadOnlyCollection<ScreenInfo> Screens
|
||||
{
|
||||
get;
|
||||
}
|
||||
@@ -67,8 +69,8 @@ public sealed class LayoutConfig
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public PointInfo ActivatedLocation
|
||||
{
|
||||
@@ -77,8 +79,19 @@ public sealed class LayoutConfig
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int ActivatedScreen
|
||||
public int ActivatedScreenIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int ActivatedScreenNumber
|
||||
{
|
||||
get;
|
||||
}
|
||||
@@ -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<RectangleInfo> 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,7 +105,7 @@ public sealed class LayoutInfo
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo ActivatedScreen
|
||||
public RectangleInfo ActivatedScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Screen object so we don't need to
|
||||
/// take a dependency on WinForms just for screen info.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -63,5 +63,6 @@
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,18 +2,20 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MouseJumpUI.NativeMethods.Core;
|
||||
namespace MouseJumpUI.NativeMethods;
|
||||
|
||||
/// <summary>
|
||||
/// A Boolean variable (should be TRUE or FALSE).
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef int BOOL;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct BOOL
|
||||
internal static partial class Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A Boolean variable (should be TRUE or FALSE).
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef int BOOL;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct BOOL
|
||||
{
|
||||
public readonly int Value;
|
||||
|
||||
public BOOL(int value)
|
||||
@@ -33,4 +35,10 @@ internal readonly struct BOOL
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The CRECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect
|
||||
/// </remarks>
|
||||
[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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to an object.
|
||||
/// This type is declared in WinNT.h as follows:
|
||||
/// typedef PVOID HANDLE;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,20 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace MouseJumpUI.NativeMethods.Core;
|
||||
namespace MouseJumpUI.NativeMethods;
|
||||
|
||||
/// <summary>
|
||||
/// A handle to a device context (DC).
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef HANDLE HDC;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct HDC
|
||||
internal static partial class Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to a device context (DC).
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef HANDLE HDC;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct HDC
|
||||
{
|
||||
public static readonly HDC Null = new(IntPtr.Zero);
|
||||
|
||||
public readonly IntPtr Value;
|
||||
@@ -26,4 +28,10 @@ internal readonly struct HDC
|
||||
}
|
||||
|
||||
public bool IsNull => this.Value == HDC.Null.Value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.GetType().Name}({this.Value})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to a display monitor.
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// if(WINVER >= 0x0500) typedef HANDLE HMONITOR;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,20 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace MouseJumpUI.NativeMethods.Core;
|
||||
namespace MouseJumpUI.NativeMethods;
|
||||
|
||||
/// <summary>
|
||||
/// A handle to a window.
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef HANDLE HWND;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct HWND
|
||||
internal static partial class Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to a window.
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef HANDLE HWND;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
internal readonly struct HWND
|
||||
{
|
||||
public static readonly HWND Null = new(IntPtr.Zero);
|
||||
|
||||
public readonly IntPtr Value;
|
||||
@@ -26,4 +28,10 @@ internal readonly struct HWND
|
||||
}
|
||||
|
||||
public bool IsNull => this.Value == HWND.Null.Value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.GetType().Name}({this.Value})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A 32-bit signed integer.The range is -2147483648 through 2147483647 decimal.
|
||||
/// This type is declared in WinNT.h as follows:
|
||||
/// typedef long LONG;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A message parameter.
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef LONG_PTR LPARAM;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<POINT>(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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The POINT structure defines the x- and y-coordinates of a point.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point
|
||||
/// </remarks>
|
||||
[SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")]
|
||||
internal readonly struct POINT
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the x-coordinate of the point.
|
||||
/// </summary>
|
||||
public readonly LONG x;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the y-coordinate of the point.
|
||||
/// </summary>
|
||||
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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect
|
||||
/// </remarks>
|
||||
[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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// An unsigned INT. The range is 0 through 4294967295 decimal.
|
||||
/// This type is declared in WinDef.h as follows:
|
||||
/// typedef unsigned int UINT;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||
/// </remarks>
|
||||
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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ internal static partial class Gdi32
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt
|
||||
/// </remarks>
|
||||
public enum ROP_CODE : uint
|
||||
internal enum ROP_CODE : uint
|
||||
{
|
||||
BLACKNESS = 0x00000042,
|
||||
CAPTUREBLT = 0x40000000,
|
||||
@@ -12,7 +12,7 @@ internal static partial class Gdi32
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode
|
||||
/// </remarks>
|
||||
public enum STRETCH_BLT_MODE : int
|
||||
internal enum STRETCH_BLT_MODE : int
|
||||
{
|
||||
BLACKONWHITE = 1,
|
||||
COLORONCOLOR = 3,
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.Gdi32)]
|
||||
public static partial int SetStretchBltMode(
|
||||
internal static partial int SetStretchBltMode(
|
||||
HDC hdc,
|
||||
STRETCH_BLT_MODE mode);
|
||||
}
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.Gdi32)]
|
||||
public static partial BOOL StretchBlt(
|
||||
internal static partial BOOL StretchBlt(
|
||||
HDC hdcDest,
|
||||
int xDest,
|
||||
int yDest,
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// If the function fails, the return value is zero.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
internal static partial BOOL EnumDisplayMonitors(
|
||||
HDC hdc,
|
||||
LPCRECT lprcClip,
|
||||
MONITORENUMPROC lpfnEnum,
|
||||
LPARAM dwData);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The GetMonitorInfo function retrieves information about a display monitor.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// If the function fails, the return value is zero.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
internal static partial BOOL GetMonitorInfoW(
|
||||
HMONITOR hMonitor,
|
||||
LPMONITORINFO lpmi);
|
||||
}
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
public static partial HDC GetWindowDC(
|
||||
internal static partial HDC GetWindowDC(
|
||||
HWND hWnd);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
|
||||
/// </remarks>
|
||||
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<MONITORINFO>(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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A MonitorEnumProc function is an application-defined callback function that is called by the EnumDisplayMonitors function.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc
|
||||
/// </remarks>
|
||||
internal delegate BOOL MONITORENUMPROC(
|
||||
HMONITOR unnamedParam1,
|
||||
HDC unnamedParam2,
|
||||
LPRECT unnamedParam3,
|
||||
LPARAM unnamedParam4);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo
|
||||
/// </remarks>
|
||||
[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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines the function's return value if the point is not contained within any display monitor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
|
||||
/// </remarks>
|
||||
internal enum MONITOR_FROM_FLAGS : uint
|
||||
{
|
||||
MONITOR_DEFAULTTONULL = 0x00000000,
|
||||
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
|
||||
MONITOR_DEFAULTTONEAREST = 0x00000002,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of flags that represent attributes of the display monitor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo
|
||||
/// </remarks>
|
||||
internal enum MONITOR_INFO_FLAGS : uint
|
||||
{
|
||||
MONITORINFOF_PRIMARY = 1,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The MonitorFromPoint function retrieves a handle to the display monitor that contains a specified point.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 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.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
internal static partial HMONITOR MonitorFromPoint(
|
||||
POINT pt,
|
||||
MONITOR_FROM_FLAGS dwFlags);
|
||||
}
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
public static partial int ReleaseDC(
|
||||
internal static partial int ReleaseDC(
|
||||
HWND hWnd,
|
||||
HDC hDC);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a simulated message generated by an input device
|
||||
/// other than a keyboard or mouse.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput
|
||||
/// </remarks>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
|
||||
/// </remarks>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the input event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
|
||||
/// </remarks>
|
||||
internal enum INPUT_TYPE : uint
|
||||
{
|
||||
INPUT_MOUSE = 0,
|
||||
INPUT_KEYBOARD = 1,
|
||||
INPUT_HARDWARE = 2,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a simulated keyboard event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
|
||||
/// </remarks>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
|
||||
/// </remarks>
|
||||
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<INPUT>(this.Value);
|
||||
}
|
||||
|
||||
public IEnumerable<INPUT> ToStructure(int count)
|
||||
{
|
||||
var ptr = this.Value;
|
||||
var size = INPUT.Size;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
yield return Marshal.PtrToStructure<INPUT>(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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a simulated mouse event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
|
||||
/// </remarks>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
|
||||
/// </remarks>
|
||||
[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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Synthesizes keystrokes, mouse motions, and button clicks.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 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.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32, SetLastError = true)]
|
||||
internal static partial UINT SendInput(
|
||||
UINT cInputs,
|
||||
LPINPUT pInputs,
|
||||
int cbSize);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the position of the mouse cursor, in screen coordinates.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns nonzero if successful or zero otherwise.
|
||||
/// To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32, SetLastError = true)]
|
||||
internal static partial BOOL GetCursorPos(
|
||||
LPPOINT lpPoint);
|
||||
}
|
||||
@@ -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
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
public static partial HWND GetDesktopWindow();
|
||||
internal static partial HWND GetDesktopWindow();
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the specified system metric or system configuration setting.
|
||||
///
|
||||
/// Note that all dimensions retrieved by GetSystemMetrics are in pixels.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 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.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32)]
|
||||
internal static partial int GetSystemMetrics(
|
||||
SYSTEM_METRICS_INDEX smIndex);
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns nonzero if successful or zero otherwise.
|
||||
/// To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
|
||||
/// </remarks>
|
||||
[LibraryImport(Libraries.User32, SetLastError = true)]
|
||||
internal static partial BOOL SetCursorPos(
|
||||
int X,
|
||||
int Y);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@
|
||||
// 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;
|
||||
|
||||
@@ -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>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3158,4 +3158,21 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="PastePlain_ShortcutWarning.Title" xml:space="preserve">
|
||||
<value>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.</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize.Header" xml:space="preserve">
|
||||
<value>Thumbnail Size</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix.Text" xml:space="preserve">
|
||||
<value>Constrain thumbnail image size to a maximum of</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix.Text" xml:space="preserve">
|
||||
<value>pixels</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Height.Header" xml:space="preserve">
|
||||
<value>Maximum height (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Width.Header" xml:space="preserve">
|
||||
<value>Maximum width (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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);
|
||||
|
||||
@@ -229,6 +229,81 @@
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
HotkeySettings="{x:Bind Path=ViewModel.MouseJumpActivationShortcut, Mode=TwoWay}" />
|
||||
</labs:SettingsCard>
|
||||
<labs:SettingsCard
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}">
|
||||
<labs:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix"
|
||||
Margin="0,0,4,0"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,5,4,0"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix"
|
||||
Margin="0,0,4,0"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
</StackPanel>
|
||||
</labs:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<Button
|
||||
x:Uid="EditButton"
|
||||
Width="40"
|
||||
Height="36"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="EditTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout x:Uid="MouseJumpThumbnailSize_Edit">
|
||||
<StackPanel Margin="0,12,0,0" Spacing="16">
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Width"
|
||||
Width="140"
|
||||
Minimum="160"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=TwoWay}" />
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Height"
|
||||
Width="140"
|
||||
Minimum="120"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</labs:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MousePointerCrosshairs">
|
||||
|
||||
Reference in New Issue
Block a user