chore: import upstream snapshot with attribution
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:16:02 +08:00
commit 79031da543
8235 changed files with 1365916 additions and 0 deletions
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
namespace MouseJump.Common.Helpers;
public static class ConfigHelper
{
public static Color? ToUnnamedColor(Color? value)
{
if (!value.HasValue)
{
return null;
}
var color = value.Value;
return Color.FromArgb(color.A, color.R, color.G, color.B);
}
public static string? SerializeToConfigColorString(Color? value)
{
if (!value.HasValue)
{
return null;
}
var color = value.Value;
return color switch
{
Color { IsNamedColor: true } =>
$"{nameof(Color)}.{color.Name}",
Color { IsSystemColor: true } =>
$"{nameof(SystemColors)}.{color.Name}",
_ =>
$"#{color.R:X2}{color.G:X2}{color.B:X2}",
};
}
public static Color? DeserializeFromConfigColorString(string? value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
// e.g. "#AABBCC"
if (value.StartsWith('#'))
{
var culture = CultureInfo.InvariantCulture;
if ((value.Length == 7)
&& int.TryParse(value[1..3], NumberStyles.HexNumber, culture, out var r)
&& int.TryParse(value[3..5], NumberStyles.HexNumber, culture, out var g)
&& int.TryParse(value[5..7], NumberStyles.HexNumber, culture, out var b))
{
return Color.FromArgb(0xFF, r, g, b);
}
}
const StringComparison comparison = StringComparison.InvariantCulture;
// e.g. "Color.Red"
const string colorPrefix = $"{nameof(Color)}.";
if (value.StartsWith(colorPrefix, comparison))
{
var colorName = value[colorPrefix.Length..];
var property = typeof(Color).GetProperties()
.SingleOrDefault(property => property.Name == colorName);
if (property is not null)
{
return (Color?)property.GetValue(null, null);
}
}
// e.g. "SystemColors.Highlight"
const string systemColorPrefix = $"{nameof(SystemColors)}.";
if (value.StartsWith(systemColorPrefix, comparison))
{
var colorName = value[systemColorPrefix.Length..];
var property = typeof(SystemColors).GetProperties()
.SingleOrDefault(property => property.Name == colorName);
if (property is not null)
{
return (Color?)property.GetValue(null, null);
}
}
return null;
}
}
@@ -0,0 +1,250 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using MouseJump.Common.Imaging;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.Models.Layout;
using MouseJump.Common.Models.Styles;
namespace MouseJump.Common.Helpers;
public static class DrawingHelper
{
public static Bitmap RenderPreview(
PreviewLayout previewLayout,
IImageRegionCopyService imageCopyService,
Action<Bitmap>? previewImageCreatedCallback = null,
Action? previewImageUpdatedCallback = null)
{
var stopwatch = Stopwatch.StartNew();
// initialize the preview image
var previewBounds = previewLayout.PreviewBounds.OuterBounds.ToRectangle();
var previewImage = new Bitmap(previewBounds.Width, previewBounds.Height, PixelFormat.Format32bppPArgb);
var previewGraphics = Graphics.FromImage(previewImage);
previewImageCreatedCallback?.Invoke(previewImage);
DrawingHelper.DrawRaisedBorder(previewGraphics, previewLayout.PreviewStyle.CanvasStyle, previewLayout.PreviewBounds);
DrawingHelper.DrawBackgroundFill(
previewGraphics,
previewLayout.PreviewStyle.CanvasStyle,
previewLayout.PreviewBounds,
[]);
// sort the source and target screen areas into the order we want to
// draw them, putting the activated screen first (we need to capture
// and draw the activated screen before we show the form because
// otherwise we'll capture the form as part of the screenshot!)
var sourceScreens = new List<RectangleInfo> { previewLayout.Screens[previewLayout.ActivatedScreenIndex] }
.Concat(previewLayout.Screens.Where((_, idx) => idx != previewLayout.ActivatedScreenIndex))
.ToList();
var targetScreens = new List<BoxBounds> { previewLayout.ScreenshotBounds[previewLayout.ActivatedScreenIndex] }
.Concat(previewLayout.ScreenshotBounds.Where((_, idx) => idx != previewLayout.ActivatedScreenIndex))
.ToList();
// draw all the screenshot bezels
foreach (var screenshotBounds in previewLayout.ScreenshotBounds)
{
DrawingHelper.DrawRaisedBorder(
previewGraphics, previewLayout.PreviewStyle.ScreenStyle, screenshotBounds);
}
var refreshRequired = false;
var placeholdersDrawn = false;
for (var i = 0; i < sourceScreens.Count; i++)
{
imageCopyService.CopyImageRegion(previewGraphics, sourceScreens[i], targetScreens[i].ContentBounds);
refreshRequired = true;
// 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 (stopwatch.ElapsedMilliseconds > 250)
{
// draw placeholder backgrounds for any undrawn screens
if (!placeholdersDrawn)
{
DrawingHelper.DrawScreenPlaceholders(
previewGraphics,
previewLayout.PreviewStyle.ScreenStyle,
targetScreens.GetRange(i + 1, targetScreens.Count - i - 1));
placeholdersDrawn = true;
}
previewImageUpdatedCallback?.Invoke();
refreshRequired = false;
}
}
if (refreshRequired)
{
previewImageUpdatedCallback?.Invoke();
}
stopwatch.Stop();
return previewImage;
}
/// <summary>
/// Draws a border shape with an optional raised 3d highlight and shadow effect.
/// </summary>
private static void DrawRaisedBorder(
Graphics graphics, BoxStyle boxStyle, BoxBounds boxBounds)
{
var borderStyle = boxStyle.BorderStyle;
if ((borderStyle.Horizontal == 0) || (borderStyle.Vertical == 0))
{
return;
}
if (borderStyle.Color is null)
{
return;
}
// draw the main box border
using var borderBrush = new SolidBrush(borderStyle.Color.Value);
var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle());
borderRegion.Exclude(boxBounds.PaddingBounds.ToRectangle());
graphics.FillRegion(borderBrush, borderRegion);
// draw the highlight and shadow
var bounds = boxBounds.BorderBounds.ToRectangle();
using var highlight = new Pen(Color.FromArgb(0x44, 0xFF, 0xFF, 0xFF));
using var shadow = new Pen(Color.FromArgb(0x44, 0x00, 0x00, 0x00));
var outer = (
Left: bounds.Left,
Top: bounds.Top,
Right: bounds.Right - 1,
Bottom: bounds.Bottom - 1
);
var inner = (
Left: bounds.Left + (int)borderStyle.Left - 1,
Top: bounds.Top + (int)borderStyle.Top - 1,
Right: bounds.Right - (int)borderStyle.Right,
Bottom: bounds.Bottom - (int)borderStyle.Bottom
);
for (var i = 0; i < borderStyle.Depth; i++)
{
// left edge
if (borderStyle.Left >= i * 2)
{
graphics.DrawLine(highlight, outer.Left, outer.Top, outer.Left, outer.Bottom);
graphics.DrawLine(shadow, inner.Left, inner.Top, inner.Left, inner.Bottom);
}
// top edge
if (borderStyle.Top >= i * 2)
{
graphics.DrawLine(highlight, outer.Left, outer.Top, outer.Right, outer.Top);
graphics.DrawLine(shadow, inner.Left, inner.Top, inner.Right, inner.Top);
}
// right edge
if (borderStyle.Right >= i * 2)
{
graphics.DrawLine(highlight, inner.Right, inner.Top, inner.Right, inner.Bottom);
graphics.DrawLine(shadow, outer.Right, outer.Top, outer.Right, outer.Bottom);
}
// bottom edge
if (borderStyle.Bottom >= i * 2)
{
graphics.DrawLine(highlight, inner.Left, inner.Bottom, inner.Right, inner.Bottom);
graphics.DrawLine(shadow, outer.Left, outer.Bottom, outer.Right, outer.Bottom);
}
// shrink the outer border for the next iteration
outer = (
outer.Left + 1,
outer.Top + 1,
outer.Right - 1,
outer.Bottom - 1
);
// enlarge the inner border for the next iteration
inner = (
inner.Left - 1,
inner.Top - 1,
inner.Right + 1,
inner.Bottom + 1
);
}
}
/// <summary>
/// Draws a gradient-filled background shape.
/// </summary>
private static void DrawBackgroundFill(
Graphics graphics, BoxStyle boxStyle, BoxBounds boxBounds, IEnumerable<RectangleInfo> excludeBounds)
{
var backgroundBounds = boxBounds.PaddingBounds;
using var backgroundBrush = DrawingHelper.GetBackgroundStyleBrush(boxStyle.BackgroundStyle, backgroundBounds);
if (backgroundBrush == null)
{
return;
}
// it's faster to build a region with the screen areas excluded
// and fill that than it is to fill the entire bounding rectangle
var backgroundRegion = new Region(backgroundBounds.ToRectangle());
foreach (var exclude in excludeBounds)
{
backgroundRegion.Exclude(exclude.ToRectangle());
}
graphics.FillRegion(backgroundBrush, backgroundRegion);
}
/// <summary>
/// Draws placeholder background images for the specified screens on the preview.
/// </summary>
private static void DrawScreenPlaceholders(
Graphics graphics, BoxStyle screenStyle, IList<BoxBounds> screenBounds)
{
if (screenBounds.Count == 0)
{
return;
}
if (screenStyle?.BackgroundStyle?.Color1 == null)
{
return;
}
using var brush = new SolidBrush(screenStyle.BackgroundStyle.Color1.Value);
graphics.FillRectangles(brush, screenBounds.Select(bounds => bounds.PaddingBounds.ToRectangle()).ToArray());
}
private static Brush? GetBackgroundStyleBrush(BackgroundStyle backgroundStyle, RectangleInfo backgroundBounds)
{
var backgroundBrush = backgroundStyle switch
{
{ Color1: not null, Color2: not null } =>
/* draw a gradient fill if both colors are specified */
new LinearGradientBrush(
backgroundBounds.ToRectangle(),
backgroundStyle.Color1.Value,
backgroundStyle.Color2.Value,
LinearGradientMode.ForwardDiagonal),
{ Color1: not null } =>
/* draw a solid fill if only one color is specified */
new SolidBrush(
backgroundStyle.Color1.Value),
{ Color2: not null } =>
/* draw a solid fill if only one color is specified */
new SolidBrush(
backgroundStyle.Color2.Value),
_ => (Brush?)null,
};
return backgroundBrush;
}
}
@@ -0,0 +1,153 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.Models.Layout;
using MouseJump.Common.Models.Styles;
namespace MouseJump.Common.Helpers;
public static class LayoutHelper
{
public static PreviewLayout GetPreviewLayout(
PreviewStyle previewStyle, List<RectangleInfo> screens, PointInfo activatedLocation)
{
ArgumentNullException.ThrowIfNull(previewStyle);
ArgumentNullException.ThrowIfNull(screens);
if (screens.Count == 0)
{
throw new ArgumentException("Value must contain at least one item.", nameof(screens));
}
var builder = new PreviewLayout.Builder();
builder.Screens = screens.ToList();
// calculate the bounding rectangle for the virtual screen
builder.VirtualScreen = LayoutHelper.GetCombinedScreenBounds(builder.Screens);
// find the screen that contains the activated location - this is the
// one we'll show the preview form on
var activatedScreen = builder.Screens.Single(
screen => screen.Contains(activatedLocation));
builder.ActivatedScreenIndex = builder.Screens.IndexOf(activatedScreen);
// work out the maximum allowed size of the preview form:
// * can't be bigger than the activated screen
// * can't be bigger than the configured canvas size
var maxPreviewSize = activatedScreen.Size
.Intersect(previewStyle.CanvasSize);
// the "content area" (i.e. drawing area) for screenshots is inside the
// preview border and inside the preview padding (if any)
var maxContentSize = maxPreviewSize
.Shrink(previewStyle.CanvasStyle.MarginStyle)
.Shrink(previewStyle.CanvasStyle.BorderStyle)
.Shrink(previewStyle.CanvasStyle.PaddingStyle);
// work out the actual size of the "content area" by scaling the virtual screen
// to fit inside the maximum content area while maintaining its aspect ration.
// we'll also offset it to allow for any margins, borders and padding
var contentBounds = builder.VirtualScreen.Size
.ScaleToFit(maxContentSize, out var scalingRatio)
.Round()
.Clamp(maxContentSize)
.PlaceAt(0, 0)
.Offset(previewStyle.CanvasStyle.MarginStyle.Left, previewStyle.CanvasStyle.MarginStyle.Top)
.Offset(previewStyle.CanvasStyle.BorderStyle.Left, previewStyle.CanvasStyle.BorderStyle.Top)
.Offset(previewStyle.CanvasStyle.PaddingStyle.Left, previewStyle.CanvasStyle.PaddingStyle.Top);
// now we know the actual size of the content area we can work outwards to
// get the size of the background bounds including margins, borders and padding
builder.PreviewStyle = previewStyle;
builder.PreviewBounds = LayoutHelper.GetBoxBoundsFromContentBounds(
contentBounds,
previewStyle.CanvasStyle);
// ... and then the size and position of the preview form on the activated screen
// * center the form to the activated position, but nudge it back
// inside the visible area of the activated screen if it falls outside
var formBounds = builder.PreviewBounds.OuterBounds
.Center(activatedLocation)
.Clamp(activatedScreen);
builder.FormBounds = formBounds;
// now calculate the positions of each of the screenshot images on the preview
builder.ScreenshotBounds = builder.Screens
.Select(
screen => LayoutHelper.GetBoxBoundsFromOuterBounds(
screen
.Offset(builder.VirtualScreen.Location.ToSize().Invert())
.Scale(scalingRatio)
.Offset(builder.PreviewBounds.ContentBounds.Location.ToSize())
.Round(),
previewStyle.ScreenStyle))
.ToList();
return builder.Build();
}
public static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
{
return screens.Skip(1).Aggregate(
seed: screens.First(),
(bounds, screen) => bounds.Union(screen));
}
/// <summary>
/// Calculates the bounds of the various areas of a box, given the content bounds and the box style.
/// Starts with the content bounds and works outward, enlarging the content bounds by the padding, border, and margin sizes to calculate the outer bounds of the box.
/// </summary>
/// <param name="contentBounds">The content bounds of the box.</param>
/// <param name="boxStyle">The style of the box, which includes the sizes of the margin, border, and padding areas.</param>
/// <returns>A <see cref="BoxBounds"/> object that represents the bounds of the different areas of the box.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="contentBounds"/> or <paramref name="boxStyle"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when any of the styles in <paramref name="boxStyle"/> is null.</exception>
public static BoxBounds GetBoxBoundsFromContentBounds(
RectangleInfo contentBounds,
BoxStyle boxStyle)
{
ArgumentNullException.ThrowIfNull(contentBounds);
ArgumentNullException.ThrowIfNull(boxStyle);
if (boxStyle.PaddingStyle == null || boxStyle.BorderStyle == null || boxStyle.MarginStyle == null)
{
throw new ArgumentException(null, nameof(boxStyle));
}
var paddingBounds = contentBounds.Enlarge(boxStyle.PaddingStyle);
var borderBounds = paddingBounds.Enlarge(boxStyle.BorderStyle);
var marginBounds = borderBounds.Enlarge(boxStyle.MarginStyle);
var outerBounds = marginBounds;
return new(
outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds);
}
/// <summary>
/// Calculates the bounds of the various areas of a box, given the outer bounds and the box style.
/// This method starts with the outer bounds and works inward, shrinking the outer bounds by the margin, border, and padding sizes to calculate the content bounds of the box.
/// </summary>
/// <param name="outerBounds">The outer bounds of the box.</param>
/// <param name="boxStyle">The style of the box, which includes the sizes of the margin, border, and padding areas.</param>
/// <returns>A <see cref="BoxBounds"/> object that represents the bounds of the different areas of the box.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="outerBounds"/> or <paramref name="boxStyle"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when any of the styles in <paramref name="boxStyle"/> is null.</exception>
public static BoxBounds GetBoxBoundsFromOuterBounds(
RectangleInfo outerBounds,
BoxStyle boxStyle)
{
ArgumentNullException.ThrowIfNull(outerBounds);
ArgumentNullException.ThrowIfNull(boxStyle);
if (outerBounds == null || boxStyle.MarginStyle == null || boxStyle.BorderStyle == null || boxStyle.PaddingStyle == null)
{
throw new ArgumentException(null, nameof(boxStyle));
}
var marginBounds = outerBounds;
var borderBounds = marginBounds.Shrink(boxStyle.MarginStyle);
var paddingBounds = borderBounds.Shrink(boxStyle.BorderStyle);
var contentBounds = paddingBounds.Shrink(boxStyle.PaddingStyle);
return new(
outerBounds, marginBounds, borderBounds, paddingBounds, contentBounds);
}
}
@@ -0,0 +1,144 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ComponentModel;
using System.Runtime.InteropServices;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.NativeMethods;
using static MouseJump.Common.NativeMethods.Core;
using static MouseJump.Common.NativeMethods.User32;
namespace MouseJump.Common.Helpers;
public static class MouseHelper
{
/// <summary>
/// Calculates where to move the cursor to by projecting a point from
/// the preview image onto the desktop and using that as the target location.
/// </summary>
/// <remarks>
/// The preview image origin is (0, 0) but the desktop origin may be non-zero,
/// or even negative if the primary monitor is not the at the top-left of the
/// entire desktop rectangle, so results may contain negative coordinates.
/// </remarks>
public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds)
{
return previewLocation
.Scale(previewSize.ScaleToFitRatio(desktopBounds.Size))
.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 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
// monitors - e.g. when trying to move the cursor from (a) to (b) we can
// *sometimes* - for no clear reason - end up at (c) instead.
//
// +----------------+
// |(c) (b) |
// | |
// | |
// | |
// +---------+ |
// | (a) | |
// +---------+----------------+
//
// setting the position again seems to fix this and moves the
// cursor to the expected location (b)
var target = location.ToPoint();
for (var i = 0; i < 2; i++)
{
var result = User32.SetCursorPos(target.X, target.Y);
if (!result)
{
throw new Win32Exception(
Marshal.GetLastWin32Error());
}
var current = MouseHelper.GetCursorPosition();
if ((current.X == target.X) || (current.Y == target.Y))
{
break;
}
}
// temporary workaround for issue #1273
MouseHelper.SimulateMouseMovementEvent(location);
}
/// <summary>
/// Sends an input simulating an absolute mouse move to the new location.
/// </summary>
/// <remarks>
/// See https://github.com/microsoft/PowerToys/issues/24523
/// https://github.com/microsoft/PowerToys/pull/24527
/// </remarks>
private static void SimulateMouseMovementEvent(PointInfo location)
{
var inputs = new User32.INPUT[]
{
new(
type: INPUT_TYPE.INPUT_MOUSE,
data: new INPUT.DUMMYUNIONNAME(
mi: new MOUSEINPUT(
dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X),
dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y),
mouseData: 0,
dwFlags: MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE,
time: 0,
dwExtraInfo: ULONG_PTR.Null))),
};
var result = User32.SendInput(
(UINT)inputs.Length,
new LPINPUT(inputs),
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(SYSTEM_METRICS_INDEX.SM_CXSCREEN);
}
private static decimal CalculateAbsoluteCoordinateY(decimal y)
{
// If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535.
// see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
return (y * 65535) / User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN);
}
}
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ComponentModel;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.NativeMethods;
using static MouseJump.Common.NativeMethods.Core;
using static MouseJump.Common.NativeMethods.User32;
namespace MouseJump.Common.Helpers;
public static class ScreenHelper
{
/// <summary>
/// Duplicates functionality available in System.Windows.Forms.SystemInformation
/// to reduce the dependency on WinForms
/// </summary>
private static RectangleInfo GetVirtualScreen()
{
return new(
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN),
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN),
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN),
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN));
}
public static IEnumerable<ScreenInfo> GetAllScreens()
{
// enumerate the monitors attached to the system
var hMonitors = new List<HMONITOR>();
var callback = new User32.MONITORENUMPROC(
(hMonitor, hdcMonitor, lprcMonitor, dwData) =>
{
hMonitors.Add(hMonitor);
return true;
});
var result = User32.EnumDisplayMonitors(HDC.Null, LPCRECT.Null, callback, LPARAM.Null);
if (!result)
{
throw new Win32Exception(
result.Value,
$"{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((DWORD)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0));
result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr);
if (!result)
{
throw new Win32Exception(
result.Value,
$"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}");
}
var monitorInfo = monitorInfoPtr.ToStructure();
monitorInfoPtr.Free();
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 ScreenInfo GetScreenFromPoint(
List<ScreenInfo> screens,
PointInfo pt)
{
// get the monitor handle from the point
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}");
}
// find the screen with the given monitor handle
var screen = screens
.Single(item => item.Handle == hMonitor);
return screen;
}
}
@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.Models.Styles;
namespace MouseJump.Common.Helpers;
public static class StyleHelper
{
/// <summary>
/// Compact (legacy) preview style
/// </summary>
public static readonly PreviewStyle CompactPreviewStyle = new(
canvasSize: new(
width: 1600,
height: 1200
),
canvasStyle: new(
marginStyle: MarginStyle.Empty,
borderStyle: new(
color: SystemColors.Highlight,
all: 6,
depth: 0
),
paddingStyle: new(
all: 0
),
backgroundStyle: new(
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
)
),
screenStyle: new(
marginStyle: new(
all: 0
),
borderStyle: new(
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
all: 0,
depth: 0
),
paddingStyle: PaddingStyle.Empty,
backgroundStyle: new(
color1: Color.MidnightBlue,
color2: Color.MidnightBlue
)
)
);
/// <summary>
/// Bezelled preview style
/// </summary>
public static readonly PreviewStyle BezelledPreviewStyle = new(
canvasSize: new(
width: 1600,
height: 1200
),
canvasStyle: new(
marginStyle: MarginStyle.Empty,
borderStyle: new(
color: SystemColors.Highlight,
all: 6,
depth: 0
),
paddingStyle: new(
all: 4
),
backgroundStyle: new(
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
)
),
screenStyle: new(
marginStyle: new(
all: 4
),
borderStyle: new(
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
all: 12,
depth: 4
),
paddingStyle: PaddingStyle.Empty,
backgroundStyle: new(
color1: Color.MidnightBlue,
color2: Color.MidnightBlue
)
)
);
public static PreviewStyle WithCanvasSize(this PreviewStyle previewStyle, SizeInfo canvasSize)
{
ArgumentNullException.ThrowIfNull(previewStyle);
ArgumentNullException.ThrowIfNull(canvasSize);
return new PreviewStyle(
canvasSize: canvasSize,
canvasStyle: previewStyle.CanvasStyle,
screenStyle: previewStyle.ScreenStyle);
}
}
@@ -0,0 +1,122 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.NativeMethods;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.Imaging;
/// <summary>
/// Implements an IImageRegionCopyService that uses the current desktop window as the copy source.
/// This is used during the main application runtime to generate preview images of the desktop.
/// </summary>
public sealed class DesktopImageRegionCopyService : IImageRegionCopyService
{
/// <summary>
/// Copies the source region from the current desktop window
/// to the target region on the specified Graphics object.
/// </summary>
public void CopyImageRegion(
Graphics targetGraphics,
RectangleInfo sourceBounds,
RectangleInfo targetBounds)
{
var stopwatch = Stopwatch.StartNew();
var (desktopHwnd, desktopHdc) = DesktopImageRegionCopyService.GetDesktopDeviceContext();
var previewHdc = DesktopImageRegionCopyService.GetGraphicsDeviceContext(
targetGraphics, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE);
stopwatch.Stop();
var source = sourceBounds.ToRectangle();
var target = targetBounds.ToRectangle();
var result = Gdi32.StretchBlt(
previewHdc,
target.X,
target.Y,
target.Width,
target.Height,
desktopHdc,
source.X,
source.Y,
source.Width,
source.Height,
Gdi32.ROP_CODE.SRCCOPY);
if (!result)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
}
// we need to release the graphics device context handle before anything
// else tries to use the Graphics object otherwise it'll give an error
// from GDI saying "Object is currently in use elsewhere"
DesktopImageRegionCopyService.FreeGraphicsDeviceContext(targetGraphics, ref previewHdc);
DesktopImageRegionCopyService.FreeDesktopDeviceContext(ref desktopHwnd, ref desktopHdc);
}
private static (HWND DesktopHwnd, HDC DesktopHdc) GetDesktopDeviceContext()
{
var desktopHwnd = User32.GetDesktopWindow();
var desktopHdc = User32.GetWindowDC(desktopHwnd);
if (desktopHdc.IsNull)
{
throw new InvalidOperationException(
$"{nameof(User32.GetWindowDC)} returned null");
}
return (desktopHwnd, desktopHdc);
}
private static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
{
if (!desktopHwnd.IsNull && !desktopHdc.IsNull)
{
var result = User32.ReleaseDC(desktopHwnd, desktopHdc);
if (result == 0)
{
throw new InvalidOperationException(
$"{nameof(User32.ReleaseDC)} returned {result}");
}
}
desktopHwnd = HWND.Null;
desktopHdc = HDC.Null;
}
/// <summary>
/// Checks if the target device context handle exists, and creates a new one from the
/// specified Graphics object if not.
/// </summary>
private static HDC GetGraphicsDeviceContext(Graphics graphics, Gdi32.STRETCH_BLT_MODE mode)
{
var graphicsHdc = (HDC)graphics.GetHdc();
var result = Gdi32.SetStretchBltMode(graphicsHdc, mode);
if (result == 0)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.SetStretchBltMode)} returned {result}");
}
return graphicsHdc;
}
/// <summary>
/// Free the specified device context handle if it exists.
/// </summary>
private static void FreeGraphicsDeviceContext(Graphics graphics, ref HDC graphicsHdc)
{
if (graphicsHdc.IsNull)
{
return;
}
graphics.ReleaseHdc(graphicsHdc.Value);
graphicsHdc = HDC.Null;
}
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using MouseJump.Common.Models.Drawing;
namespace MouseJump.Common.Imaging;
public interface IImageRegionCopyService
{
/// <summary>
/// Copies the source region from the provider's source image (e.g. the interactive desktop,
/// a static image, etc.) to the target region on the specified Graphics object.
/// </summary>
/// <remarks>
/// Implementations of this interface are used to capture regions of the interactive desktop
/// during runtime, or to capture regions of a static reference image during unit tests.
/// </remarks>
void CopyImageRegion(
Graphics targetGraphics,
RectangleInfo sourceBounds,
RectangleInfo targetBounds);
}
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing.Drawing2D;
using MouseJump.Common.Models.Drawing;
namespace MouseJump.Common.Imaging;
/// <summary>
/// Implements an IImageRegionCopyService that uses the specified image as the copy source.
/// This is used for testing the DrawingHelper rather than as part of the main application.
/// </summary>
public sealed class StaticImageRegionCopyService : IImageRegionCopyService
{
public StaticImageRegionCopyService(Image sourceImage)
{
this.SourceImage = sourceImage ?? throw new ArgumentNullException(nameof(sourceImage));
}
private Image SourceImage
{
get;
}
/// <summary>
/// Copies the source region from the static source image
/// to the target region on the specified Graphics object.
/// </summary>
public void CopyImageRegion(
Graphics targetGraphics,
RectangleInfo sourceBounds,
RectangleInfo targetBounds)
{
// prevent the background bleeding through into screen images
// (see https://github.com/mikeclayton/FancyMouse/issues/44)
targetGraphics.PixelOffsetMode = PixelOffsetMode.Half;
targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
targetGraphics.DrawImage(
image: this.SourceImage,
destRect: targetBounds.ToRectangle(),
srcRect: sourceBounds.ToRectangle(),
srcUnit: GraphicsUnit.Pixel);
}
}
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Drawing;
public sealed class BoxBounds
{
/*
see https://www.w3schools.com/css/css_boxmodel.asp
+--------------[bounds]---------------+
|▒▒▒▒▒▒▒▒▒▒▒▒▒▒[margin]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒|
|▒▒▓▓▓▓▓▓▓▓▓▓▓▓[border]▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒|
|▒▒▓▓░░░░░░░░░░[padding]░░░░░░░░░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ [content] ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▒▒|
|▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒|
|▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒|
+-------------------------------------+
*/
public BoxBounds(
RectangleInfo outerBounds,
RectangleInfo marginBounds,
RectangleInfo borderBounds,
RectangleInfo paddingBounds,
RectangleInfo contentBounds)
{
this.OuterBounds = outerBounds ?? throw new ArgumentNullException(nameof(outerBounds));
this.MarginBounds = marginBounds ?? throw new ArgumentNullException(nameof(marginBounds));
this.BorderBounds = borderBounds ?? throw new ArgumentNullException(nameof(borderBounds));
this.PaddingBounds = paddingBounds ?? throw new ArgumentNullException(nameof(paddingBounds));
this.ContentBounds = contentBounds ?? throw new ArgumentNullException(nameof(contentBounds));
}
/// <summary>
/// Gets the outer bounds of this layout box.
/// </summary>
public RectangleInfo OuterBounds
{
get;
}
public RectangleInfo MarginBounds
{
get;
}
public RectangleInfo BorderBounds
{
get;
}
public RectangleInfo PaddingBounds
{
get;
}
/// <summary>
/// Gets the bounds of the content area for this layout box.
/// </summary>
public RectangleInfo ContentBounds
{
get;
}
}
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Point object with some extra utility methods.
/// </summary>
public sealed class PointInfo
{
public PointInfo(decimal x, decimal y)
{
this.X = x;
this.Y = y;
}
public PointInfo(Point point)
: this(point.X, point.Y)
{
}
public decimal X
{
get;
}
public decimal Y
{
get;
}
/// <summary>
/// Moves this PointInfo inside the specified RectangleInfo.
/// </summary>
public PointInfo Clamp(RectangleInfo outer)
{
return new(
x: Math.Clamp(this.X, outer.X, outer.Right),
y: Math.Clamp(this.Y, outer.Y, outer.Bottom));
}
public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor);
public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y);
public Point ToPoint() => new((int)this.X, (int)this.Y);
public SizeInfo ToSize()
{
return new((int)this.X, (int)this.Y);
}
/// <summary>
/// Stretches the point to the same proportional position in targetBounds as
/// it currently is in sourceBounds
/// </summary>
public PointInfo Stretch(RectangleInfo source, RectangleInfo target)
{
return new PointInfo(
x: ((this.X - source.X) / source.Width * target.Width) + target.X,
y: ((this.Y - source.Y) / source.Height * target.Height) + target.Y);
}
public PointInfo Truncate() =>
new(
(int)this.X,
(int)this.Y);
public override string ToString()
{
return "{" +
$"{nameof(this.X)}={this.X}," +
$"{nameof(this.Y)}={this.Y}" +
"}";
}
}
@@ -0,0 +1,308 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using MouseJump.Common.Models.Styles;
using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle;
namespace MouseJump.Common.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods.
/// </summary>
public sealed class RectangleInfo
{
public static readonly RectangleInfo Empty = new(0, 0, 0, 0);
public RectangleInfo(decimal x, decimal y, decimal width, decimal height)
{
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
public RectangleInfo(Rectangle rectangle)
: this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)
{
}
public RectangleInfo(Point location, SizeInfo size)
: this(location.X, location.Y, size.Width, size.Height)
{
}
public RectangleInfo(SizeInfo size)
: this(0, 0, size.Width, size.Height)
{
}
public decimal X
{
get;
}
public decimal Y
{
get;
}
public decimal Width
{
get;
}
public decimal Height
{
get;
}
[JsonIgnore]
public decimal Left =>
this.X;
[JsonIgnore]
public decimal Top =>
this.Y;
[JsonIgnore]
public decimal Right =>
this.X + this.Width;
[JsonIgnore]
public decimal Bottom =>
this.Y + this.Height;
[JsonIgnore]
public decimal Area =>
this.Width * this.Height;
[JsonIgnore]
public PointInfo Location =>
new(this.X, this.Y);
[JsonIgnore]
public PointInfo Midpoint =>
new(
x: this.X + (this.Width / 2),
y: this.Y + (this.Height / 2));
[JsonIgnore]
public SizeInfo Size => new(this.Width, this.Height);
/// <summary>
/// Centers the rectangle around a specified point.
/// </summary>
/// <param name="point">The <see cref="PointInfo"/> around which the rectangle will be centered.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is centered around the specified point.</returns>
public RectangleInfo Center(PointInfo point) =>
new(
x: point.X - (this.Width / 2),
y: point.Y - (this.Height / 2),
width: this.Width,
height: this.Height);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is moved within the bounds of the specified outer rectangle.
/// If the current rectangle is larger than the outer rectangle, an exception is thrown.
/// </summary>
/// <param name="outer">The outer <see cref="RectangleInfo"/> within which to confine this rectangle.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is the result of moving this rectangle within the bounds of the outer rectangle.</returns>
/// <exception cref="ArgumentException">Thrown when the current rectangle is larger than the outer rectangle.</exception>
public RectangleInfo Clamp(RectangleInfo outer)
{
if ((this.Width > outer.Width) || (this.Height > outer.Height))
{
throw new ArgumentException($"Value cannot be larger than {nameof(outer)}.");
}
return new(
x: Math.Clamp(this.X, outer.X, outer.Right - this.Width),
y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height),
width: this.Width,
height: this.Height);
}
/// <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(decimal x, decimal y) =>
this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + 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(PointInfo pt) =>
this.Contains(pt.X, pt.Y);
/// <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);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is larger than the current rectangle.
/// The dimensions of the new rectangle are calculated by enlarging the current rectangle's dimensions by the size of the border.
/// </summary>
/// <param name="border">The <see cref="BorderStyle"/> that specifies the amount to enlarge the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is larger than the current rectangle by the specified border amounts.</returns>
public RectangleInfo Enlarge(BorderStyle border) =>
new(
this.X - border.Left,
this.Y - border.Top,
this.Width + border.Horizontal,
this.Height + border.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is larger than the current rectangle.
/// The dimensions of the new rectangle are calculated by enlarging the current rectangle's dimensions by the size of the margin.
/// </summary>
/// <param name="margin">The <see cref="MarginStyle"/> that specifies the amount to enlarge the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is larger than the current rectangle by the specified margin amounts.</returns>
public RectangleInfo Enlarge(MarginStyle margin) =>
new(
this.X - margin.Left,
this.Y - margin.Top,
this.Width + margin.Horizontal,
this.Height + margin.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is larger than the current rectangle.
/// The dimensions of the new rectangle are calculated by enlarging the current rectangle's dimensions by the size of the padding.
/// </summary>
/// <param name="padding">The <see cref="PaddingStyle"/> that specifies the amount to enlarge the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is larger than the current rectangle by the specified padding amounts.</returns>
public RectangleInfo Enlarge(PaddingStyle padding) =>
new(
this.X - padding.Left,
this.Y - padding.Top,
this.Width + padding.Horizontal,
this.Height + padding.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is offset by the specified amount.
/// </summary>
/// <param name="amount">The <see cref="SizeInfo"/> representing the amount to offset in both the X and Y directions.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is offset by the specified amount.</returns>
public RectangleInfo Offset(SizeInfo amount) =>
this.Offset(amount.Width, amount.Height);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is offset by the specified X and Y distances.
/// </summary>
/// <param name="dx">The distance to offset the rectangle along the X-axis.</param>
/// <param name="dy">The distance to offset the rectangle along the Y-axis.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is offset by the specified X and Y distances.</returns>
public RectangleInfo Offset(decimal dx, decimal dy) =>
new(this.X + dx, this.Y + dy, this.Width, this.Height);
public RectangleInfo Round() =>
this.Round(0);
public RectangleInfo Round(int decimals) => new(
Math.Round(this.X, decimals),
Math.Round(this.Y, decimals),
Math.Round(this.Width, decimals),
Math.Round(this.Height, decimals));
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is a scaled version of the current rectangle.
/// The dimensions of the new rectangle are calculated by multiplying the current rectangle's dimensions by the scaling factor.
/// </summary>
/// <param name="scalingFactor">The factor by which to scale the rectangle's dimensions.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is a scaled version of the current rectangle.</returns>
public RectangleInfo Scale(decimal scalingFactor) =>
new(
this.X * scalingFactor,
this.Y * scalingFactor,
this.Width * scalingFactor,
this.Height * scalingFactor);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is smaller than the current rectangle.
/// The dimensions of the new rectangle are calculated by shrinking the current rectangle's dimensions by the size of the border.
/// </summary>
/// <param name="border">The <see cref="BorderStyle"/> that specifies the amount to shrink the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is smaller than the current rectangle by the specified border amounts.</returns>
public RectangleInfo Shrink(BorderStyle border) =>
new(
this.X + border.Left,
this.Y + border.Top,
this.Width - border.Horizontal,
this.Height - border.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is smaller than the current rectangle.
/// The dimensions of the new rectangle are calculated by shrinking the current rectangle's dimensions by the size of the margin.
/// </summary>
/// <param name="margin">The <see cref="MarginStyle"/> that specifies the amount to shrink the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is smaller than the current rectangle by the specified margin amounts.</returns>
public RectangleInfo Shrink(MarginStyle margin) =>
new(
this.X + margin.Left,
this.Y + margin.Top,
this.Width - margin.Horizontal,
this.Height - margin.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is smaller than the current rectangle.
/// The dimensions of the new rectangle are calculated by shrinking the current rectangle's dimensions by the size of the padding.
/// </summary>
/// <param name="padding">The <see cref="PaddingStyle"/> that specifies the amount to shrink the rectangle on each side.</param>
/// <returns>A new <see cref="RectangleInfo"/> that is smaller than the current rectangle by the specified padding amounts.</returns>
public RectangleInfo Shrink(PaddingStyle padding) =>
new(
this.X + padding.Left,
this.Y + padding.Top,
this.Width - padding.Horizontal,
this.Height - padding.Vertical);
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> where the X, Y, Width, and Height properties of the current rectangle are truncated to integers.
/// </summary>
/// <returns>A new <see cref="RectangleInfo"/> with the X, Y, Width, and Height properties of the current rectangle truncated to integers.</returns>
public RectangleInfo Truncate() =>
new(
(int)this.X,
(int)this.Y,
(int)this.Width,
(int)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 RectangleInfo Union(RectangleInfo rect)
{
var x1 = Math.Min(this.X, rect.X);
var x2 = Math.Max(this.X + this.Width, rect.X + rect.Width);
var y1 = Math.Min(this.Y, rect.Y);
var y2 = Math.Max(this.Y + this.Height, rect.Y + rect.Height);
return new RectangleInfo(x1, y1, x2 - x1, y2 - y1);
}
public Rectangle ToRectangle() =>
new(
(int)this.X,
(int)this.Y,
(int)this.Width,
(int)this.Height);
public override string ToString()
{
return "{" +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Width)}={this.Width}," +
$"{nameof(this.Height)}={this.Height}" +
"}";
}
}
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Drawing;
/// <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
{
public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
{
// this.Handle is a HMONITOR that has been cast to an int because we don't want
// to expose the HMONITOR type outside the current assembly.
this.Handle = handle;
this.Primary = primary;
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea));
}
public int Handle
{
get;
}
public bool Primary
{
get;
}
public RectangleInfo DisplayArea
{
get;
}
public RectangleInfo WorkingArea
{
get;
}
}
@@ -0,0 +1,182 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using MouseJump.Common.Models.Styles;
using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle;
namespace MouseJump.Common.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
/// </summary>
public sealed class SizeInfo
{
public SizeInfo(decimal width, decimal height)
{
this.Width = width;
this.Height = height;
}
public SizeInfo(Size size)
: this(size.Width, size.Height)
{
}
public decimal Width
{
get;
}
public decimal Height
{
get;
}
public SizeInfo Clamp(SizeInfo max)
{
return new(
width: Math.Clamp(this.Width, 0, max.Width),
height: Math.Clamp(this.Height, 0, max.Height));
}
public SizeInfo Clamp(decimal maxWidth, decimal maxHeight)
{
return new(
width: Math.Clamp(this.Width, 0, maxWidth),
height: Math.Clamp(this.Height, 0, maxHeight));
}
public SizeInfo Enlarge(BorderStyle border) =>
new(
this.Width + border.Horizontal,
this.Height + border.Vertical);
public SizeInfo Enlarge(PaddingStyle padding) =>
new(
this.Width + padding.Horizontal,
this.Height + padding.Vertical);
/// <summary>
/// Rounds down the width and height of this size to the nearest whole number.
/// </summary>
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
public SizeInfo Floor()
{
return new SizeInfo(
Math.Floor(this.Width),
Math.Floor(this.Height));
}
/// <summary>
/// Calculates the intersection of this size with another size, resulting in a size that represents
/// the overlapping dimensions. Both sizes must be non-negative.
/// </summary>
/// <param name="size">The size to intersect with this instance.</param>
/// <returns>A new <see cref="SizeInfo"/> instance representing the intersection of the two sizes.</returns>
/// <exception cref="ArgumentException">Thrown when either this size or the specified size has negative dimensions.</exception>
public SizeInfo Intersect(SizeInfo size)
{
if ((this.Width < 0) || (this.Height < 0) || (size.Width < 0) || (size.Height < 0))
{
throw new ArgumentException("Sizes must be non-negative");
}
return new(
Math.Min(this.Width, size.Width),
Math.Min(this.Height, size.Height));
}
/// <summary>
/// Creates a new <see cref="SizeInfo"/> instance with the width and height negated, effectively inverting its dimensions.
/// </summary>
/// <returns>A new <see cref="SizeInfo"/> instance with inverted dimensions.</returns>
public SizeInfo Invert() =>
new(-this.Width, -this.Height);
/// <summary>
/// Creates a new <see cref="RectangleInfo"/> instance representing a rectangle with this size,
/// positioned at the specified coordinates.
/// </summary>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle.</param>
/// <returns>A new <see cref="RectangleInfo"/> instance representing the positioned rectangle.</returns>
public RectangleInfo PlaceAt(decimal x, decimal y) =>
new(x, y, this.Width, this.Height);
public SizeInfo Round() =>
this.Round(0);
public SizeInfo Round(int decimals) => new(
Math.Round(this.Width, decimals),
Math.Round(this.Height, decimals));
public SizeInfo Scale(decimal scalingFactor) => new(
this.Width * scalingFactor,
this.Height * scalingFactor);
/// <summary>
/// Scales this size to fit within the bounds of another size, while maintaining the aspect ratio.
/// </summary>
/// <param name="bounds">The size to fit this size into.</param>
/// <returns>A new <see cref="SizeInfo"/> instance representing the scaled size.</returns>
public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingRatio)
{
var widthRatio = bounds.Width / this.Width;
var heightRatio = bounds.Height / this.Height;
switch (widthRatio.CompareTo(heightRatio))
{
case < 0:
scalingRatio = widthRatio;
return new(bounds.Width, this.Height * widthRatio);
case 0:
// widthRatio and heightRatio are the same, so just pick one
scalingRatio = widthRatio;
return bounds;
case > 0:
scalingRatio = heightRatio;
return new(this.Width * heightRatio, bounds.Height);
}
}
/// <summary>
/// Calculates the scaling ratio needed to fit this size within the bounds of another size without distorting the aspect ratio.
/// </summary>
/// <param name="bounds">The size to fit this size into.</param>
/// <returns>The scaling ratio as a decimal.</returns>
/// <exception cref="ArgumentException">Thrown if the width or height of the bounds is zero.</exception>
public decimal ScaleToFitRatio(SizeInfo bounds)
{
if (bounds.Width == 0 || bounds.Height == 0)
{
throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds));
}
var widthRatio = bounds.Width / this.Width;
var heightRatio = bounds.Height / this.Height;
var scalingRatio = Math.Min(widthRatio, heightRatio);
return scalingRatio;
}
public SizeInfo Shrink(BorderStyle border) =>
new(this.Width - border.Horizontal, this.Height - border.Vertical);
public SizeInfo Shrink(MarginStyle margin) =>
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
public SizeInfo Shrink(PaddingStyle padding) =>
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
public Size ToSize() => new((int)this.Width, (int)this.Height);
public Point ToPoint() => new((int)this.Width, (int)this.Height);
public override string ToString()
{
return "{" +
$"{nameof(this.Width)}={this.Width}," +
$"{nameof(this.Height)}={this.Height}" +
"}";
}
}
@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.Models.Styles;
namespace MouseJump.Common.Models.Layout;
public sealed class PreviewLayout
{
public sealed class Builder
{
public Builder()
{
this.Screens = new();
this.ScreenshotBounds = new();
}
public PreviewStyle? PreviewStyle
{
get;
set;
}
public RectangleInfo? VirtualScreen
{
get;
set;
}
public List<RectangleInfo> Screens
{
get;
set;
}
public int ActivatedScreenIndex
{
get;
set;
}
public RectangleInfo? FormBounds
{
get;
set;
}
public BoxBounds? PreviewBounds
{
get;
set;
}
public List<BoxBounds> ScreenshotBounds
{
get;
set;
}
public PreviewLayout Build()
{
return new PreviewLayout(
previewStyle: this.PreviewStyle ?? throw new InvalidOperationException($"{nameof(this.PreviewStyle)} must be initialized before calling {nameof(this.Build)}."),
virtualScreen: this.VirtualScreen ?? throw new InvalidOperationException($"{nameof(this.VirtualScreen)} must be initialized before calling {nameof(this.Build)}."),
screens: this.Screens ?? throw new InvalidOperationException($"{nameof(this.Screens)} must be initialized before calling {nameof(this.Build)}."),
activatedScreenIndex: this.ActivatedScreenIndex,
formBounds: this.FormBounds ?? throw new InvalidOperationException($"{nameof(this.FormBounds)} must be initialized before calling {nameof(this.Build)}."),
previewBounds: this.PreviewBounds ?? throw new InvalidOperationException($"{nameof(this.PreviewBounds)} must be initialized before calling {nameof(this.Build)}."),
screenshotBounds: this.ScreenshotBounds ?? throw new InvalidOperationException($"{nameof(this.ScreenshotBounds)} must be initialized before calling {nameof(this.Build)}."));
}
}
public PreviewLayout(
PreviewStyle previewStyle,
RectangleInfo virtualScreen,
List<RectangleInfo> screens,
int activatedScreenIndex,
RectangleInfo formBounds,
BoxBounds previewBounds,
List<BoxBounds> screenshotBounds)
{
this.PreviewStyle = previewStyle ?? throw new ArgumentNullException(nameof(previewStyle));
this.VirtualScreen = virtualScreen ?? throw new ArgumentNullException(nameof(virtualScreen));
this.Screens = (screens ?? throw new ArgumentNullException(nameof(screens)))
.ToList().AsReadOnly();
this.ActivatedScreenIndex = activatedScreenIndex;
this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds));
this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds));
this.ScreenshotBounds = (screenshotBounds ?? throw new ArgumentNullException(nameof(screenshotBounds)))
.ToList().AsReadOnly();
}
public PreviewStyle PreviewStyle
{
get;
}
public RectangleInfo VirtualScreen
{
get;
}
public ReadOnlyCollection<RectangleInfo> Screens
{
get;
}
public int ActivatedScreenIndex
{
get;
}
public RectangleInfo FormBounds
{
get;
}
public BoxBounds PreviewBounds
{
get;
}
public ReadOnlyCollection<BoxBounds> ScreenshotBounds
{
get;
}
}
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Settings;
public enum PreviewType
{
Custom = 0,
Compact = 1,
Bezelled = 2,
}
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Styles;
/// <summary>
/// Represents the background fill style for a drawing object.
/// </summary>
public sealed class BackgroundStyle
{
public static readonly BackgroundStyle Empty = new(
Color.Transparent,
Color.Transparent
);
public BackgroundStyle(
Color? color1,
Color? color2)
{
this.Color1 = color1;
this.Color2 = color2;
}
public Color? Color1
{
get;
}
public Color? Color2
{
get;
}
public override string ToString()
{
return "{" +
$"{nameof(this.Color1)}={this.Color1}," +
$"{nameof(this.Color2)}={this.Color2}" +
"}";
}
}
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Styles;
/// <summary>
/// Represents the border style for a drawing object.
/// </summary>
public sealed class BorderStyle
{
public static readonly BorderStyle Empty = new(null, 0, 0);
public BorderStyle(Color? color, decimal all, decimal depth)
: this(color, all, all, all, all, depth)
{
}
public BorderStyle(Color? color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
{
this.Color = color;
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
this.Depth = depth;
}
public Color? Color
{
get;
}
public decimal Left
{
get;
}
public decimal Top
{
get;
}
public decimal Right
{
get;
}
public decimal Bottom
{
get;
}
/// <summary>
/// Gets the "depth" of the 3d highlight and shadow effect on the border.
/// </summary>
public decimal Depth
{
get;
}
public decimal Horizontal => this.Left + this.Right;
public decimal Vertical => this.Top + this.Bottom;
public override string ToString()
{
return "{" +
$"{nameof(this.Color)}={this.Color}," +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Right)}={this.Right}," +
$"{nameof(this.Bottom)}={this.Bottom}," +
$"{nameof(this.Depth)}={this.Depth}" +
"}";
}
}
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Styles;
/// <summary>
/// Represents the styles to apply to a simple box-layout based drawing object.
/// </summary>
public sealed class BoxStyle
{
/*
see https://www.w3schools.com/css/css_boxmodel.asp
+--------------[bounds]---------------+
|▒▒▒▒▒▒▒▒▒▒▒▒▒▒[margin]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒|
|▒▒▓▓▓▓▓▓▓▓▓▓▓▓[border]▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒|
|▒▒▓▓░░░░░░░░░░[padding]░░░░░░░░░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ [content] ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░ ░░▓▓▒▒|
|▒▒▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▒▒|
|▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒|
|▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒|
+-------------------------------------+
*/
public static readonly BoxStyle Empty = new(MarginStyle.Empty, BorderStyle.Empty, PaddingStyle.Empty, BackgroundStyle.Empty);
public BoxStyle(
MarginStyle marginStyle,
BorderStyle borderStyle,
PaddingStyle paddingStyle,
BackgroundStyle backgroundStyle)
{
this.MarginStyle = marginStyle ?? throw new ArgumentNullException(nameof(marginStyle));
this.BorderStyle = borderStyle ?? throw new ArgumentNullException(nameof(borderStyle));
this.PaddingStyle = paddingStyle ?? throw new ArgumentNullException(nameof(paddingStyle));
this.BackgroundStyle = backgroundStyle ?? throw new ArgumentNullException(nameof(backgroundStyle));
}
/// <summary>
/// Gets the margin style for this layout box.
/// </summary>
public MarginStyle MarginStyle
{
get;
}
/// <summary>
/// Gets the border style for this layout box.
/// </summary>
public BorderStyle BorderStyle
{
get;
}
/// <summary>
/// Gets the padding style for this layout box.
/// </summary>
public PaddingStyle PaddingStyle
{
get;
}
/// <summary>
/// Gets the background fill style for the content area of this layout box.
/// </summary>
public BackgroundStyle BackgroundStyle
{
get;
}
}
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Styles;
/// <summary>
/// Represents the margin style for a drawing object.
/// </summary>
public sealed class MarginStyle
{
public static readonly MarginStyle Empty = new(0);
public MarginStyle(decimal all)
: this(all, all, all, all)
{
}
public MarginStyle(decimal left, decimal top, decimal right, decimal bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
public decimal Left
{
get;
}
public decimal Top
{
get;
}
public decimal Right
{
get;
}
public decimal Bottom
{
get;
}
public decimal Horizontal => this.Left + this.Right;
public decimal Vertical => this.Top + this.Bottom;
public override string ToString()
{
return "{" +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Right)}={this.Right}," +
$"{nameof(this.Bottom)}={this.Bottom}" +
"}";
}
}
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.Models.Styles;
/// <summary>
/// Represents the margin style for a drawing object.
/// </summary>
public sealed class PaddingStyle
{
public static readonly PaddingStyle Empty = new(0);
public PaddingStyle(decimal all)
: this(all, all, all, all)
{
}
public PaddingStyle(decimal left, decimal top, decimal right, decimal bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
public decimal Left
{
get;
}
public decimal Top
{
get;
}
public decimal Right
{
get;
}
public decimal Bottom
{
get;
}
public decimal Horizontal => this.Left + this.Right;
public decimal Vertical => this.Top + this.Bottom;
public override string ToString()
{
return "{" +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Right)}={this.Right}," +
$"{nameof(this.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.
using MouseJump.Common.Models.Drawing;
namespace MouseJump.Common.Models.Styles;
public sealed class PreviewStyle
{
public PreviewStyle(
SizeInfo canvasSize,
BoxStyle canvasStyle,
BoxStyle screenStyle)
{
this.CanvasSize = canvasSize ?? throw new ArgumentNullException(nameof(canvasSize));
this.CanvasStyle = canvasStyle ?? throw new ArgumentNullException(nameof(canvasStyle));
this.ScreenStyle = screenStyle ?? throw new ArgumentNullException(nameof(screenStyle));
}
public SizeInfo CanvasSize
{
get;
}
public BoxStyle CanvasStyle
{
get;
}
public BoxStyle ScreenStyle
{
get;
}
}
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ProjectGuid>{923DF87C-CA99-4D1C-B1D2-959174E95BFA}</ProjectGuid>
<AssemblyName>PowerToys.MouseJump.Common</AssemblyName>
<AssemblyTitle>PowerToys.MouseJump.Common</AssemblyTitle>
<AssemblyDescription>PowerToys MouseJump.Common</AssemblyDescription>
<OutputType>Library</OutputType>
<OutputPath>$(RepoRoot)$(Platform)\$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
@@ -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.
namespace MouseJump.Common.NativeMethods;
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)
{
this.Value = value;
}
public BOOL(bool value)
{
this.Value = value ? 1 : 0;
}
public static implicit operator bool(BOOL value) => value.Value != 0;
public static implicit operator BOOL(bool value) => new(value);
public static implicit operator int(BOOL value) => value.Value;
public static implicit operator BOOL(int value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace MouseJump.Common.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(
int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public CRECT(
LONG left, LONG top, LONG right, LONG bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public static int Size =>
Marshal.SizeOf(typeof(CRECT));
public override string ToString()
{
return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}";
}
}
}
@@ -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.Runtime.InteropServices;
namespace MouseJump.Common.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 int Size =>
Marshal.SizeOf(typeof(DWORD));
public static implicit operator uint(DWORD value) => value.Value;
public static implicit operator DWORD(uint value) => new(value);
public static explicit operator int(DWORD value) => (int)value.Value;
public static explicit operator DWORD(int value) => new((uint)value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -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.
namespace MouseJump.Common.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 explicit operator HANDLE(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -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.
namespace MouseJump.Common.NativeMethods;
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;
public HDC(IntPtr value)
{
this.Value = value;
}
public bool IsNull => this.Value == HDC.Null.Value;
public static implicit operator IntPtr(HDC value) => value.Value;
public static explicit operator HDC(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -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.
namespace MouseJump.Common.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 explicit operator HMONITOR(int value) => new(value);
public static implicit operator IntPtr(HMONITOR value) => value.Value;
public static explicit operator HMONITOR(IntPtr value) => new(value);
public static implicit operator HANDLE(HMONITOR value) => new(value.Value);
public static explicit operator HMONITOR(HANDLE value) => new(value.Value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
namespace MouseJump.Common.NativeMethods;
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);
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Name and value taken from Win32Api")]
public static readonly HWND HWND_MESSAGE = new(-3);
public readonly IntPtr Value;
public HWND(IntPtr value)
{
this.Value = value;
}
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 MouseJump.Common.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,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.
namespace MouseJump.Common.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 bool IsNull => this.Value == LPARAM.Null.Value;
public static implicit operator IntPtr(LPARAM value) => value.Value;
public static explicit operator LPARAM(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class Core
{
internal readonly struct LPCRECT
{
public static readonly LPCRECT Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPCRECT(IntPtr value)
{
this.Value = value;
}
public LPCRECT(CRECT value)
{
this.Value = LPCRECT.ToPtr(value);
}
public bool IsNull => this.Value == LPCRECT.Null.Value;
private static IntPtr ToPtr(CRECT value)
{
var ptr = Marshal.AllocHGlobal(CRECT.Size);
Marshal.StructureToPtr(value, ptr, false);
return ptr;
}
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public static implicit operator IntPtr(LPCRECT value) => value.Value;
public static explicit operator LPCRECT(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class Core
{
internal readonly struct LPPOINT
{
public static readonly LPPOINT Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPPOINT(IntPtr value)
{
this.Value = value;
}
public LPPOINT(POINT value)
{
this.Value = LPPOINT.ToPtr(value);
}
public bool IsNull => this.Value == LPPOINT.Null.Value;
private static IntPtr ToPtr(POINT value)
{
var ptr = Marshal.AllocHGlobal(POINT.Size);
Marshal.StructureToPtr(value, ptr, false);
return ptr;
}
public POINT ToStructure()
{
return Marshal.PtrToStructure<POINT>(this.Value);
}
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public static implicit operator IntPtr(LPPOINT value) => value.Value;
public static explicit operator LPPOINT(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class Core
{
internal readonly struct LPRECT
{
public static readonly LPRECT Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPRECT(IntPtr value)
{
this.Value = value;
}
public LPRECT(RECT value)
{
this.Value = LPRECT.ToPtr(value);
}
public bool IsNull => this.Value == LPRECT.Null.Value;
private static IntPtr ToPtr(RECT value)
{
var ptr = Marshal.AllocHGlobal(RECT.Size);
Marshal.StructureToPtr(value, ptr, false);
return ptr;
}
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public static implicit operator IntPtr(LPRECT value) => value.Value;
public static explicit operator LPRECT(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace MouseJump.Common.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(
int x,
int y)
{
this.x = x;
this.y = y;
}
public POINT(
LONG x,
LONG y)
{
this.x = x;
this.y = y;
}
public static int Size =>
Marshal.SizeOf(typeof(POINT));
public override string ToString()
{
return $"x={this.x},y={this.y}";
}
}
}
@@ -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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace MouseJump.Common.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(
int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(
LONG left, LONG top, LONG right, LONG bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public static int Size =>
Marshal.SizeOf(typeof(RECT));
public override string ToString()
{
return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}";
}
}
}
@@ -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.
namespace MouseJump.Common.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 static explicit operator int(UINT value) => (int)value.Value;
public static explicit operator UINT(int value) => new((uint)value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.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 explicit 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 MouseJump.Common.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})";
}
}
}
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.NativeMethods;
internal static partial class Gdi32
{
/// <summary>
/// A raster-operation code. These codes define how the color data for the source
/// rectangle is to be combined with the color data for the destination rectangle
/// to achieve the final color.
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt
/// </remarks>
internal enum ROP_CODE : uint
{
BLACKNESS = 0x00000042,
CAPTUREBLT = 0x40000000,
DSTINVERT = 0x00550009,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
NOMIRRORBITMAP = 0x80000000,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
PATCOPY = 0x00F00021,
PATINVERT = 0x005A0049,
PATPAINT = 0x00FB0A09,
SRCAND = 0x008800C6,
SRCCOPY = 0x00CC0020,
SRCERASE = 0x00440328,
SRCINVERT = 0x00660046,
SRCPAINT = 0x00EE0086,
WHITENESS = 0x00FF0062,
}
}
@@ -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.
namespace MouseJump.Common.NativeMethods;
internal static partial class Gdi32
{
/// <summary>
/// The stretching mode.
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode
/// </remarks>
internal enum STRETCH_BLT_MODE : int
{
BLACKONWHITE = 1,
COLORONCOLOR = 3,
HALFTONE = 4,
WHITEONBLACK = 2,
STRETCH_ANDSCANS = STRETCH_BLT_MODE.BLACKONWHITE,
STRETCH_DELETESCANS = STRETCH_BLT_MODE.COLORONCOLOR,
STRETCH_HALFTONE = STRETCH_BLT_MODE.HALFTONE,
STRETCH_ORSCANS = STRETCH_BLT_MODE.WHITEONBLACK,
}
}
@@ -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 MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class Gdi32
{
/// <summary>
/// The SetStretchBltMode function sets the bitmap stretching mode in the specified device context.
/// </summary>
/// <returns>
/// If the function succeeds, the return value is the previous stretching mode.
/// If the function fails, the return value is zero.
/// This function can return the following value.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode
/// </remarks>
[LibraryImport(Libraries.Gdi32)]
internal static partial int SetStretchBltMode(
HDC hdc,
STRETCH_BLT_MODE mode);
}
@@ -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.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class Gdi32
{
/// <summary>
/// The StretchBlt function copies a bitmap from a source rectangle into a destination
/// rectangle, stretching or compressing the bitmap to fit the dimensions of the
/// destination rectangle, if necessary. The system stretches or compresses the bitmap
/// according to the stretching mode currently set in the destination device context.
/// </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/wingdi/nf-wingdi-stretchblt
/// </remarks>
[LibraryImport(Libraries.Gdi32)]
internal static partial BOOL StretchBlt(
HDC hdcDest,
int xDest,
int yDest,
int wDest,
int hDest,
HDC hdcSrc,
int xSrc,
int ySrc,
int wSrc,
int hSrc,
ROP_CODE rop);
}
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Common.NativeMethods;
internal static class Libraries
{
public const string Gdi32 = "gdi32";
public const string User32 = "user32";
}
@@ -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.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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);
}
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <summary>
/// The GetWindowDC function retrieves the device context (DC) for the entire window,
/// including title bar, menus, and scroll bars. A window device context permits painting
/// anywhere in a window, because the origin of the device context is the upper-left
/// corner of the window instead of the client area.
///
/// GetWindowDC assigns default attributes to the window device context each time it
/// retrieves the device context. Previous attributes are lost.
/// </summary>
/// <returns>
/// If the function succeeds, the return value is a handle to a device context for the specified window.
/// If the function fails, the return value is NULL, indicating an error or an invalid hWnd parameter.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc
/// </remarks>
[LibraryImport(Libraries.User32)]
internal static partial HDC GetWindowDC(
HWND hWnd);
}
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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 MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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,40 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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(MONITORINFO));
}
}
@@ -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 MouseJump.Common.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 MouseJump.Common.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,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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);
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <summary>
/// The ReleaseDC function releases a device context (DC), freeing it for use by other
/// applications. The effect of the ReleaseDC function depends on the type of DC. It
/// frees only common and window DCs. It has no effect on class or private DCs.
/// </summary>
/// <returns>
/// The return value indicates whether the DC was released. If the DC was released, the return value is 1.
/// If the DC was not released, the return value is zero.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc
/// </remarks>
[LibraryImport(Libraries.User32)]
internal static partial int ReleaseDC(
HWND hWnd,
HDC hDC);
}
@@ -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 MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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 MouseJump.Common.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,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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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,70 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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, false);
ptr += size;
}
return mem;
}
public void Free()
{
Marshal.FreeHGlobal(this.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;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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,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.Diagnostics.CodeAnalysis;
namespace MouseJump.Common.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,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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,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 MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.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);
}
@@ -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 MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <summary>
/// Retrieves a handle to the desktop window. The desktop window covers the entire
/// screen. The desktop window is the area on top of which other windows are painted.
/// </summary>
/// <returns>
/// The return value is a handle to the desktop window.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow
/// </remarks>
[LibraryImport(Libraries.User32)]
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 MouseJump.Common.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 MouseJump.Common.NativeMethods;
[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")]
internal static partial class User32
{
internal enum SYSTEM_METRICS_INDEX : uint
{
SM_ARRANGE = 56,
SM_CLEANBOOT = 67,
SM_CMONITORS = 80,
SM_CMOUSEBUTTONS = 43,
SM_CONVERTIBLESLATEMODE = 0x2003,
SM_CXBORDER = 5,
SM_CXCURSOR = 13,
SM_CXDLGFRAME = 7,
SM_CXDOUBLECLK = 36,
SM_CXDRAG = 68,
SM_CXEDGE = 45,
SM_CXFIXEDFRAME = SM_CXDLGFRAME,
SM_CXFOCUSBORDER = 83,
SM_CXFRAME = 32,
SM_CXFULLSCREEN = 16,
SM_CXHSCROLL = 21,
SM_CXHTHUMB = 10,
SM_CXICON = 11,
SM_CXICONSPACING = 38,
SM_CXMAXIMIZED = 61,
SM_CXMAXTRACK = 59,
SM_CXMENUCHECK = 71,
SM_CXMENUSIZE = 54,
SM_CXMIN = 28,
SM_CXMINIMIZED = 57,
SM_CXMINSPACING = 47,
SM_CXMINTRACK = 34,
SM_CXPADDEDBORDER = 92,
SM_CXSCREEN = 0,
SM_CXSIZE = 30,
SM_CXSIZEFRAME = SM_CXFRAME,
SM_CXSMICON = 49,
SM_CXSMSIZE = 52,
SM_CXVIRTUALSCREEN = 78,
SM_CXVSCROLL = 2,
SM_CYBORDER = 6,
SM_CYCAPTION = 4,
SM_CYCURSOR = 14,
SM_CYDLGFRAME = 8,
SM_CYDOUBLECLK = 37,
SM_CYDRAG = 69,
SM_CYEDGE = 46,
SM_CYFIXEDFRAME = SM_CYDLGFRAME,
SM_CYFOCUSBORDER = 84,
SM_CYFRAME = 33,
SM_CYFULLSCREEN = 17,
SM_CYHSCROLL = 3,
SM_CYICON = 12,
SM_CYICONSPACING = 39,
SM_CYKANJIWINDOW = 18,
SM_CYMAXIMIZED = 62,
SM_CYMAXTRACK = 60,
SM_CYMENU = 15,
SM_CYMENUCHECK = 72,
SM_CYMENUSIZE = 55,
SM_CYMIN = 29,
SM_CYMINIMIZED = 58,
SM_CYMINSPACING = 48,
SM_CYMINTRACK = 35,
SM_CYSCREEN = 1,
SM_CYSIZE = 31,
SM_CYSIZEFRAME = SM_CYFRAME,
SM_CYSMCAPTION = 51,
SM_CYSMICON = 50,
SM_CYSMSIZE = 53,
SM_CYVIRTUALSCREEN = 79,
SM_CYVSCROLL = 20,
SM_CYVTHUMB = 9,
SM_DBCSENABLED = 42,
SM_DEBUG = 22,
SM_DIGITIZER = 94,
SM_IMMENABLED = 82,
SM_MAXIMUMTOUCHES = 95,
SM_MEDIACENTER = 87,
SM_MENUDROPALIGNMENT = 40,
SM_MIDEASTENABLED = 74,
SM_MOUSEPRESENT = 19,
SM_MOUSEHORIZONTALWHEELPRESENT = 91,
SM_MOUSEWHEELPRESENT = 75,
SM_NETWORK = 63,
SM_PENWINDOWS = 41,
SM_REMOTECONTROL = 0x2001,
SM_REMOTESESSION = 0x1000,
SM_SAMEDISPLAYFORMA = 81,
SM_SECURE = 44,
SM_SERVERR2 = 89,
SM_SHOWSOUNDS = 70,
SM_SHUTTINGDOWN = 0x2000,
SM_SLOWMACHINE = 73,
SM_STARTER = 88,
SM_SWAPBUTTON = 23,
SM_SYSTEMDOCKED = 0x2004,
SM_TABLETPC = 86,
SM_XVIRTUALSCREEN = 76,
SM_YVIRTUALSCREEN = 77,
}
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Common.NativeMethods.Core;
namespace MouseJump.Common.NativeMethods;
internal static partial class User32
{
/// <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);
}
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MouseJump.Common.UnitTests")]