mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
* [Mouse Jump] - screenshot performance (#24607) * [Mouse Jump] - add words to spellchecker (#24607) * [Mouse Jump] - progressive activation (#24607) * [Mouse Jump] - fixing build (#24607) * [Mouse Jump] - fixing jump location, add unit tests, refactor (#24607) * [Mouse Jump] - removed testing code (#24607) * [Mouse Jump] added failing tests for * [Mouse Jump] - fix problem with some monitor layouts (#24607) * [Mouse Jump] - cleaning up some comments and namepsaces * [Mouse Jump] - added another screen layout test (#24607)
This commit is contained in:
237
src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs
Normal file
237
src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using MouseJumpUI.Drawing.Models;
|
||||
using MouseJumpUI.NativeMethods.Core;
|
||||
using MouseJumpUI.NativeWrappers;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class DrawingHelper
|
||||
{
|
||||
public static LayoutInfo CalculateLayoutInfo(
|
||||
LayoutConfig layoutConfig)
|
||||
{
|
||||
if (layoutConfig is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(layoutConfig));
|
||||
}
|
||||
|
||||
var builder = new LayoutInfo.Builder
|
||||
{
|
||||
LayoutConfig = layoutConfig,
|
||||
};
|
||||
|
||||
builder.ActivatedScreen = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen];
|
||||
|
||||
// work out the maximum *constrained* form size
|
||||
// * can't be bigger than the activated screen
|
||||
// * can't be bigger than the max form size
|
||||
var maxFormSize = builder.ActivatedScreen.Size
|
||||
.Intersect(layoutConfig.MaximumFormSize);
|
||||
|
||||
// the drawing area for screen images is inside the
|
||||
// form border and inside the preview border
|
||||
var maxDrawingSize = maxFormSize
|
||||
.Shrink(layoutConfig.FormPadding)
|
||||
.Shrink(layoutConfig.PreviewPadding);
|
||||
|
||||
// scale the virtual screen to fit inside the drawing bounds
|
||||
var scalingRatio = layoutConfig.VirtualScreen.Size
|
||||
.ScaleToFitRatio(maxDrawingSize);
|
||||
|
||||
// position the drawing bounds inside the preview border
|
||||
var drawingBounds = layoutConfig.VirtualScreen.Size
|
||||
.ScaleToFit(maxDrawingSize)
|
||||
.PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top);
|
||||
|
||||
// now we know the size of the drawing area we can work out the preview size
|
||||
builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding);
|
||||
|
||||
// ... and the form size
|
||||
// * center the form to the activated position, but nudge it back
|
||||
// inside the visible area of the activated screen if it falls outside
|
||||
builder.FormBounds = builder.PreviewBounds.Size
|
||||
.PlaceAt(0, 0)
|
||||
.Enlarge(layoutConfig.FormPadding)
|
||||
.Center(layoutConfig.ActivatedLocation)
|
||||
.Clamp(builder.ActivatedScreen);
|
||||
|
||||
// now calculate the positions of each of the screen images on the preview
|
||||
builder.ScreenBounds = layoutConfig.ScreenBounds
|
||||
.Select(
|
||||
screen => screen
|
||||
.Offset(layoutConfig.VirtualScreen.Location.Size.Negate())
|
||||
.Scale(scalingRatio)
|
||||
.Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top))
|
||||
.ToList();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize and position the specified form.
|
||||
/// </summary>
|
||||
public static void PositionForm(
|
||||
Form form, RectangleInfo formBounds)
|
||||
{
|
||||
// note - do this in two steps rather than "this.Bounds = formBounds" as there
|
||||
// appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2,
|
||||
// where the form scaling uses either the *primary* screen scaling or the *previous*
|
||||
// screen's scaling when the form is moved to a different screen. i've got no idea
|
||||
// *why*, but the exact sequence of calls below seems to be a workaround...
|
||||
// see https://github.com/mikeclayton/FancyMouse/issues/2
|
||||
var bounds = formBounds.ToRectangle();
|
||||
form.Location = bounds.Location;
|
||||
_ = form.PointToScreen(Point.Empty);
|
||||
form.Size = bounds.Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the preview background.
|
||||
/// </summary>
|
||||
public static void DrawPreviewBackground(
|
||||
Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable<RectangleInfo> screenBounds)
|
||||
{
|
||||
using var backgroundBrush = new LinearGradientBrush(
|
||||
previewBounds.Location.ToPoint(),
|
||||
previewBounds.Size.ToPoint(),
|
||||
Color.FromArgb(13, 87, 210), // light blue
|
||||
Color.FromArgb(3, 68, 192)); // darker blue
|
||||
|
||||
// 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(previewBounds.ToRectangle());
|
||||
foreach (var screen in screenBounds)
|
||||
{
|
||||
backgroundRegion.Exclude(screen.ToRectangle());
|
||||
}
|
||||
|
||||
previewGraphics.FillRegion(backgroundBrush, backgroundRegion);
|
||||
}
|
||||
|
||||
public static void EnsureDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
|
||||
{
|
||||
if (desktopHwnd.IsNull)
|
||||
{
|
||||
desktopHwnd = User32.GetDesktopWindow();
|
||||
}
|
||||
|
||||
if (desktopHdc.IsNull)
|
||||
{
|
||||
desktopHdc = User32.GetWindowDC(desktopHwnd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
|
||||
{
|
||||
if (!desktopHwnd.IsNull && !desktopHdc.IsNull)
|
||||
{
|
||||
_ = User32.ReleaseDC(desktopHwnd, desktopHdc);
|
||||
}
|
||||
|
||||
desktopHwnd = HWND.Null;
|
||||
desktopHdc = HDC.Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the device context handle exists, and creates a new one from the
|
||||
/// Graphics object if not.
|
||||
/// </summary>
|
||||
public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc)
|
||||
{
|
||||
if (previewHdc.IsNull)
|
||||
{
|
||||
previewHdc = new HDC(previewGraphics.GetHdc());
|
||||
_ = Gdi32.SetStretchBltMode(previewHdc, MouseJumpUI.NativeMethods.Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free the specified device context handle if it exists.
|
||||
/// </summary>
|
||||
public static void FreePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc)
|
||||
{
|
||||
if ((previewGraphics is not null) && !previewHdc.IsNull)
|
||||
{
|
||||
previewGraphics.ReleaseHdc(previewHdc.Value);
|
||||
previewHdc = HDC.Null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw placeholder images for any non-activated screens on the preview.
|
||||
/// Will release the specified device context handle if it needs to draw anything.
|
||||
/// </summary>
|
||||
public static void DrawPreviewPlaceholders(
|
||||
Graphics previewGraphics, IEnumerable<RectangleInfo> screenBounds)
|
||||
{
|
||||
// we can exclude the activated screen because we've already draw
|
||||
// the screen capture image for that one on the preview
|
||||
if (screenBounds.Any())
|
||||
{
|
||||
var brush = Brushes.Black;
|
||||
previewGraphics.FillRectangles(brush, screenBounds.Select(screen => screen.ToRectangle()).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws screen captures from the specified desktop handle onto the target device context.
|
||||
/// </summary>
|
||||
public static void DrawPreviewScreen(
|
||||
HDC sourceHdc,
|
||||
HDC targetHdc,
|
||||
RectangleInfo sourceBounds,
|
||||
RectangleInfo targetBounds)
|
||||
{
|
||||
var source = sourceBounds.ToRectangle();
|
||||
var target = targetBounds.ToRectangle();
|
||||
_ = Gdi32.StretchBlt(
|
||||
targetHdc,
|
||||
target.X,
|
||||
target.Y,
|
||||
target.Width,
|
||||
target.Height,
|
||||
sourceHdc,
|
||||
source.X,
|
||||
source.Y,
|
||||
source.Width,
|
||||
source.Height,
|
||||
MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws screen captures from the specified desktop handle onto the target device context.
|
||||
/// </summary>
|
||||
public static void DrawPreviewScreens(
|
||||
HDC sourceHdc,
|
||||
HDC targetHdc,
|
||||
IList<RectangleInfo> sourceBounds,
|
||||
IList<RectangleInfo> targetBounds)
|
||||
{
|
||||
for (var i = 0; i < sourceBounds.Count; i++)
|
||||
{
|
||||
var source = sourceBounds[i].ToRectangle();
|
||||
var target = targetBounds[i].ToRectangle();
|
||||
_ = Gdi32.StretchBlt(
|
||||
targetHdc,
|
||||
target.X,
|
||||
target.Y,
|
||||
target.Width,
|
||||
target.Height,
|
||||
sourceHdc,
|
||||
source.X,
|
||||
source.Y,
|
||||
source.Width,
|
||||
source.Height,
|
||||
MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class LayoutHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Center an object on the given origin.
|
||||
/// </summary>
|
||||
public static Point CenterObject(Size obj, Point origin)
|
||||
{
|
||||
return new Point(
|
||||
x: (int)(origin.X - ((float)obj.Width / 2)),
|
||||
y: (int)(origin.Y - ((float)obj.Height / 2)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines the specified regions and returns the smallest rectangle that contains them.
|
||||
/// </summary>
|
||||
/// <param name="regions">The regions to combine.</param>
|
||||
/// <returns>
|
||||
/// Returns the smallest rectangle that contains all the specified regions.
|
||||
/// </returns>
|
||||
public static Rectangle CombineRegions(List<Rectangle> regions)
|
||||
{
|
||||
if (regions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(regions));
|
||||
}
|
||||
|
||||
if (regions.Count == 0)
|
||||
{
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
|
||||
var combined = regions.Aggregate(
|
||||
seed: regions[0],
|
||||
func: Rectangle.Union);
|
||||
return combined;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the midpoint of the given region.
|
||||
/// </summary>
|
||||
public static Point GetMidpoint(Rectangle region)
|
||||
{
|
||||
return new Point(
|
||||
(region.Left + region.Right) / 2,
|
||||
(region.Top + region.Bottom) / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the largest Size object that can fit inside
|
||||
/// all of the given sizes. (Equivalent to a Size
|
||||
/// object with the smallest Width and smallest Height from
|
||||
/// all of the specified sizes).
|
||||
/// </summary>
|
||||
public static Size IntersectSizes(params Size[] sizes)
|
||||
{
|
||||
return new Size(
|
||||
sizes.Min(s => s.Width),
|
||||
sizes.Min(s => s.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the location to move the inner rectangle so that it sits entirely inside
|
||||
/// the outer rectangle. Returns the inner rectangle's current position if it is
|
||||
/// already inside the outer rectangle.
|
||||
/// </summary>
|
||||
public static Rectangle MoveInside(Rectangle inner, Rectangle outer)
|
||||
{
|
||||
if ((inner.Width > outer.Width) || (inner.Height > outer.Height))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(inner)} cannot be larger than {nameof(outer)}.");
|
||||
}
|
||||
|
||||
return inner with
|
||||
{
|
||||
X = Math.Clamp(inner.X, outer.X, outer.Right - inner.Width),
|
||||
Y = Math.Clamp(inner.Y, outer.Y, outer.Bottom - inner.Height),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales a location within a reference region onto a new region
|
||||
/// so that it's proportionally in the same position in the new region.
|
||||
/// </summary>
|
||||
public static Point ScaleLocation(Rectangle originalBounds, Point originalLocation, Rectangle scaledBounds)
|
||||
{
|
||||
return new Point(
|
||||
(int)(originalLocation.X / (double)originalBounds.Width * scaledBounds.Width) + scaledBounds.Left,
|
||||
(int)(originalLocation.Y / (double)originalBounds.Height * scaledBounds.Height) + scaledBounds.Top);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale an object to fit inside the specified bounds while maintaining aspect ratio.
|
||||
/// </summary>
|
||||
public static Size ScaleToFit(Size obj, Size bounds)
|
||||
{
|
||||
if (bounds.Width == 0 || bounds.Height == 0)
|
||||
{
|
||||
return Size.Empty;
|
||||
}
|
||||
|
||||
var widthRatio = (double)obj.Width / bounds.Width;
|
||||
var heightRatio = (double)obj.Height / bounds.Height;
|
||||
var scaledSize = (widthRatio > heightRatio)
|
||||
? bounds with
|
||||
{
|
||||
Height = (int)(obj.Height / widthRatio),
|
||||
}
|
||||
: bounds with
|
||||
{
|
||||
Width = (int)(obj.Width / heightRatio),
|
||||
};
|
||||
return scaledSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the position to show the preview form based on a number of factors.
|
||||
/// </summary>
|
||||
/// <param name="desktopBounds">
|
||||
/// The bounds of the entire desktop / virtual screen. Might start at a negative
|
||||
/// x, y if a non-primary screen is located left of or above the primary screen.
|
||||
/// </param>
|
||||
/// <param name="activatedPosition">
|
||||
/// The current position of the cursor on the virtual desktop.
|
||||
/// </param>
|
||||
/// <param name="activatedMonitorBounds">
|
||||
/// The bounds of the screen the cursor is currently on. Might start at a negative
|
||||
/// x, y if a non-primary screen is located left of or above the primary screen.
|
||||
/// </param>
|
||||
/// <param name="maximumThumbnailImageSize">
|
||||
/// The largest allowable size of the preview image. This is literally the just
|
||||
/// image itself, not including padding around the image.
|
||||
/// </param>
|
||||
/// <param name="thumbnailImagePadding">
|
||||
/// The total width and height of padding around the preview image.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size and location to use when showing the preview image form.
|
||||
/// </returns>
|
||||
public static Rectangle GetPreviewFormBounds(
|
||||
Rectangle desktopBounds,
|
||||
Point activatedPosition,
|
||||
Rectangle activatedMonitorBounds,
|
||||
Size maximumThumbnailImageSize,
|
||||
Size thumbnailImagePadding)
|
||||
{
|
||||
// see https://learn.microsoft.com/en-gb/windows/win32/gdi/the-virtual-screen
|
||||
// calculate the maximum size the form is allowed to be
|
||||
var maxFormSize = LayoutHelper.IntersectSizes(
|
||||
new[]
|
||||
{
|
||||
// can't be bigger than the current screen
|
||||
activatedMonitorBounds.Size,
|
||||
|
||||
// can't be bigger than the max preview image
|
||||
// *plus* the padding around the preview image
|
||||
// (max thumbnail image size doesn't include the padding)
|
||||
maximumThumbnailImageSize + thumbnailImagePadding,
|
||||
});
|
||||
|
||||
// calculate the actual form size by scaling the entire
|
||||
// desktop bounds into the max thumbnail size while accounting
|
||||
// for the size of the padding around the preview
|
||||
var thumbnailImageSize = LayoutHelper.ScaleToFit(
|
||||
obj: desktopBounds.Size,
|
||||
bounds: maxFormSize - thumbnailImagePadding);
|
||||
var formSize = thumbnailImageSize + thumbnailImagePadding;
|
||||
|
||||
// center the form to the activated position, but nudge it back
|
||||
// inside the visible area of the screen if it falls outside
|
||||
var formBounds = LayoutHelper.MoveInside(
|
||||
inner: new Rectangle(
|
||||
LayoutHelper.CenterObject(
|
||||
obj: formSize,
|
||||
origin: activatedPosition),
|
||||
formSize),
|
||||
outer: activatedMonitorBounds);
|
||||
|
||||
return formBounds;
|
||||
}
|
||||
}
|
||||
@@ -67,10 +67,9 @@ namespace MouseJumpUI.Helpers
|
||||
|
||||
private static string GetCallerInfo()
|
||||
{
|
||||
StackTrace stackTrace = new StackTrace();
|
||||
|
||||
var stackTrace = new StackTrace();
|
||||
var methodName = stackTrace.GetFrame(3)?.GetMethod();
|
||||
var className = methodName?.DeclaringType.Name;
|
||||
var className = methodName?.DeclaringType?.Name;
|
||||
return "[Method]: " + methodName?.Name + " [Class]: " + className;
|
||||
}
|
||||
}
|
||||
|
||||
87
src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs
Normal file
87
src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using MouseJumpUI.Drawing.Models;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal 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>
|
||||
/// Moves the cursor to the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://github.com/mikeclayton/FancyMouse/pull/3
|
||||
/// </remarks>
|
||||
public static void JumpCursor(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 a second time seems to fix this and moves the
|
||||
// cursor to the expected location (b)
|
||||
var point = location.ToPoint();
|
||||
Cursor.Position = point;
|
||||
Cursor.Position = point;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static void SimulateMouseMovementEvent(Point location)
|
||||
{
|
||||
var mouseMoveInput = new NativeMethods.INPUT
|
||||
{
|
||||
type = NativeMethods.INPUTTYPE.INPUT_MOUSE,
|
||||
data = new NativeMethods.InputUnion
|
||||
{
|
||||
mi = new NativeMethods.MOUSEINPUT
|
||||
{
|
||||
dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X),
|
||||
dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y),
|
||||
mouseData = 0,
|
||||
dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE
|
||||
| (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE,
|
||||
time = 0,
|
||||
dwExtraInfo = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
var inputs = new NativeMethods.INPUT[] { mouseMoveInput };
|
||||
_ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size);
|
||||
}
|
||||
}
|
||||
@@ -5,108 +5,107 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Helpers
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
// Win32 functions required for temporary workaround for issue #1273
|
||||
internal static class NativeMethods
|
||||
{
|
||||
// Win32 functions required for temporary workaround for issue #1273
|
||||
internal class NativeMethods
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int GetSystemMetrics(SystemMetric smIndex);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INPUT
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
||||
internal INPUTTYPE type;
|
||||
internal InputUnion data;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int GetSystemMetrics(SystemMetric smIndex);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INPUT
|
||||
internal static int Size
|
||||
{
|
||||
internal INPUTTYPE type;
|
||||
internal InputUnion data;
|
||||
|
||||
internal static int Size
|
||||
{
|
||||
get { return Marshal.SizeOf(typeof(INPUT)); }
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct InputUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
internal MOUSEINPUT mi;
|
||||
[FieldOffset(0)]
|
||||
internal KEYBDINPUT ki;
|
||||
[FieldOffset(0)]
|
||||
internal HARDWAREINPUT hi;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct MOUSEINPUT
|
||||
{
|
||||
internal int dx;
|
||||
internal int dy;
|
||||
internal int mouseData;
|
||||
internal uint dwFlags;
|
||||
internal uint time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct KEYBDINPUT
|
||||
{
|
||||
internal short wVk;
|
||||
internal short wScan;
|
||||
internal uint dwFlags;
|
||||
internal int time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct HARDWAREINPUT
|
||||
{
|
||||
internal int uMsg;
|
||||
internal short wParamL;
|
||||
internal short wParamH;
|
||||
}
|
||||
|
||||
internal enum INPUTTYPE : uint
|
||||
{
|
||||
INPUT_MOUSE = 0,
|
||||
INPUT_KEYBOARD = 1,
|
||||
INPUT_HARDWARE = 2,
|
||||
}
|
||||
|
||||
internal enum MOUSE_INPUT_FLAGS : uint
|
||||
{
|
||||
MOUSEEVENTF_MOVE = 0x0001,
|
||||
MOUSEEVENTF_LEFTDOWN = 0x0002,
|
||||
MOUSEEVENTF_LEFTUP = 0x0004,
|
||||
MOUSEEVENTF_RIGHTDOWN = 0x0008,
|
||||
MOUSEEVENTF_RIGHTUP = 0x0010,
|
||||
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
|
||||
MOUSEEVENTF_MIDDLEUP = 0x0040,
|
||||
MOUSEEVENTF_XDOWN = 0x0080,
|
||||
MOUSEEVENTF_XUP = 0x0100,
|
||||
MOUSEEVENTF_WHEEL = 0x0800,
|
||||
MOUSEEVENTF_HWHEEL = 0x1000,
|
||||
MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000,
|
||||
MOUSEEVENTF_VIRTUALDESK = 0x4000,
|
||||
MOUSEEVENTF_ABSOLUTE = 0x8000,
|
||||
}
|
||||
|
||||
internal enum SystemMetric
|
||||
{
|
||||
SM_CXSCREEN = 0,
|
||||
SM_CYSCREEN = 1,
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateX(int x)
|
||||
{
|
||||
return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN);
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateY(int y)
|
||||
{
|
||||
return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN);
|
||||
get { return Marshal.SizeOf(typeof(INPUT)); }
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct InputUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
internal MOUSEINPUT mi;
|
||||
[FieldOffset(0)]
|
||||
internal KEYBDINPUT ki;
|
||||
[FieldOffset(0)]
|
||||
internal HARDWAREINPUT hi;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct MOUSEINPUT
|
||||
{
|
||||
internal int dx;
|
||||
internal int dy;
|
||||
internal int mouseData;
|
||||
internal uint dwFlags;
|
||||
internal uint time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct KEYBDINPUT
|
||||
{
|
||||
internal short wVk;
|
||||
internal short wScan;
|
||||
internal uint dwFlags;
|
||||
internal int time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct HARDWAREINPUT
|
||||
{
|
||||
internal int uMsg;
|
||||
internal short wParamL;
|
||||
internal short wParamH;
|
||||
}
|
||||
|
||||
internal enum INPUTTYPE : uint
|
||||
{
|
||||
INPUT_MOUSE = 0,
|
||||
INPUT_KEYBOARD = 1,
|
||||
INPUT_HARDWARE = 2,
|
||||
}
|
||||
|
||||
internal enum MOUSE_INPUT_FLAGS : uint
|
||||
{
|
||||
MOUSEEVENTF_MOVE = 0x0001,
|
||||
MOUSEEVENTF_LEFTDOWN = 0x0002,
|
||||
MOUSEEVENTF_LEFTUP = 0x0004,
|
||||
MOUSEEVENTF_RIGHTDOWN = 0x0008,
|
||||
MOUSEEVENTF_RIGHTUP = 0x0010,
|
||||
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
|
||||
MOUSEEVENTF_MIDDLEUP = 0x0040,
|
||||
MOUSEEVENTF_XDOWN = 0x0080,
|
||||
MOUSEEVENTF_XUP = 0x0100,
|
||||
MOUSEEVENTF_WHEEL = 0x0800,
|
||||
MOUSEEVENTF_HWHEEL = 0x1000,
|
||||
MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000,
|
||||
MOUSEEVENTF_VIRTUALDESK = 0x4000,
|
||||
MOUSEEVENTF_ABSOLUTE = 0x8000,
|
||||
}
|
||||
|
||||
internal enum SystemMetric
|
||||
{
|
||||
SM_CXSCREEN = 0,
|
||||
SM_CYSCREEN = 1,
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateX(int x)
|
||||
{
|
||||
return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN);
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateY(int y)
|
||||
{
|
||||
return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user