mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 11:46:30 +02:00
[MouseJump]Reduce winforms dependency, thumbnail size settings, shortcut keys(#25487)
* [Mouse Jump] - reorganise existing NativeMethods (#25482) * Mouse Jump] - reorganise Helper classes / main form code (#25482) * Mouse Jump] - replace use of System.Windows.Forms.Screen with Native Methods (#25482) * Mouse Jump] - replace use of System.Windows.Forms.SystemInformation with Native Methods (#25482) * [Mouse Jump] - replace use of System.Windows.Forms.Cursor with Native Methods (#25482) * [Mouse Jump] - improve popup responsiveness (#25484) * [Mouse Jump] - fixed spellchecker errors (#25484) * [Mouse Jump] - add settings card for thumbnail size (#24564) * [Mouse Jump] - shortcut keys to jump to centres of screens (#25069) * [Mouse Jump] - fix spelling (#25069) * [Mouse Jump] - fix spelling - numpad (#25069) * [Mouse Jump] - updated "thumbnail size" settings text (#24564)
This commit is contained in:
@@ -7,95 +7,16 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using MouseJumpUI.Drawing.Models;
|
||||
using MouseJumpUI.NativeMethods.Core;
|
||||
using MouseJumpUI.NativeWrappers;
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.NativeMethods;
|
||||
using static MouseJumpUI.NativeMethods.Core;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class DrawingHelper
|
||||
{
|
||||
public static LayoutInfo CalculateLayoutInfo(
|
||||
LayoutConfig layoutConfig)
|
||||
{
|
||||
if (layoutConfig is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(layoutConfig));
|
||||
}
|
||||
|
||||
var builder = new LayoutInfo.Builder
|
||||
{
|
||||
LayoutConfig = layoutConfig,
|
||||
};
|
||||
|
||||
builder.ActivatedScreen = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen];
|
||||
|
||||
// work out the maximum *constrained* form size
|
||||
// * can't be bigger than the activated screen
|
||||
// * can't be bigger than the max form size
|
||||
var maxFormSize = builder.ActivatedScreen.Size
|
||||
.Intersect(layoutConfig.MaximumFormSize);
|
||||
|
||||
// the drawing area for screen images is inside the
|
||||
// form border and inside the preview border
|
||||
var maxDrawingSize = maxFormSize
|
||||
.Shrink(layoutConfig.FormPadding)
|
||||
.Shrink(layoutConfig.PreviewPadding);
|
||||
|
||||
// scale the virtual screen to fit inside the drawing bounds
|
||||
var scalingRatio = layoutConfig.VirtualScreen.Size
|
||||
.ScaleToFitRatio(maxDrawingSize);
|
||||
|
||||
// position the drawing bounds inside the preview border
|
||||
var drawingBounds = layoutConfig.VirtualScreen.Size
|
||||
.ScaleToFit(maxDrawingSize)
|
||||
.PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top);
|
||||
|
||||
// now we know the size of the drawing area we can work out the preview size
|
||||
builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding);
|
||||
|
||||
// ... and the form size
|
||||
// * center the form to the activated position, but nudge it back
|
||||
// inside the visible area of the activated screen if it falls outside
|
||||
builder.FormBounds = builder.PreviewBounds.Size
|
||||
.PlaceAt(0, 0)
|
||||
.Enlarge(layoutConfig.FormPadding)
|
||||
.Center(layoutConfig.ActivatedLocation)
|
||||
.Clamp(builder.ActivatedScreen);
|
||||
|
||||
// now calculate the positions of each of the screen images on the preview
|
||||
builder.ScreenBounds = layoutConfig.ScreenBounds
|
||||
.Select(
|
||||
screen => screen
|
||||
.Offset(layoutConfig.VirtualScreen.Location.Size.Negate())
|
||||
.Scale(scalingRatio)
|
||||
.Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top))
|
||||
.ToList();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize and position the specified form.
|
||||
/// </summary>
|
||||
public static void PositionForm(
|
||||
Form form, RectangleInfo formBounds)
|
||||
{
|
||||
// note - do this in two steps rather than "this.Bounds = formBounds" as there
|
||||
// appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2,
|
||||
// where the form scaling uses either the *primary* screen scaling or the *previous*
|
||||
// screen's scaling when the form is moved to a different screen. i've got no idea
|
||||
// *why*, but the exact sequence of calls below seems to be a workaround...
|
||||
// see https://github.com/mikeclayton/FancyMouse/issues/2
|
||||
var bounds = formBounds.ToRectangle();
|
||||
form.Location = bounds.Location;
|
||||
_ = form.PointToScreen(Point.Empty);
|
||||
form.Size = bounds.Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the preview background.
|
||||
/// Draw the gradient-filled preview background.
|
||||
/// </summary>
|
||||
public static void DrawPreviewBackground(
|
||||
Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable<RectangleInfo> screenBounds)
|
||||
@@ -127,6 +48,11 @@ internal static class DrawingHelper
|
||||
if (desktopHdc.IsNull)
|
||||
{
|
||||
desktopHdc = User32.GetWindowDC(desktopHwnd);
|
||||
if (desktopHdc.IsNull)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(User32.GetWindowDC)} returned null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +60,12 @@ internal static class DrawingHelper
|
||||
{
|
||||
if (!desktopHwnd.IsNull && !desktopHdc.IsNull)
|
||||
{
|
||||
_ = User32.ReleaseDC(desktopHwnd, desktopHdc);
|
||||
var result = User32.ReleaseDC(desktopHwnd, desktopHdc);
|
||||
if (result == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(User32.ReleaseDC)} returned {result}");
|
||||
}
|
||||
}
|
||||
|
||||
desktopHwnd = HWND.Null;
|
||||
@@ -143,14 +74,20 @@ internal static class DrawingHelper
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the device context handle exists, and creates a new one from the
|
||||
/// Graphics object if not.
|
||||
/// specified Graphics object if not.
|
||||
/// </summary>
|
||||
public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc)
|
||||
{
|
||||
if (previewHdc.IsNull)
|
||||
{
|
||||
previewHdc = new HDC(previewGraphics.GetHdc());
|
||||
_ = Gdi32.SetStretchBltMode(previewHdc, MouseJumpUI.NativeMethods.Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE);
|
||||
var result = Gdi32.SetStretchBltMode(previewHdc, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(Gdi32.SetStretchBltMode)} returned {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +107,7 @@ internal static class DrawingHelper
|
||||
/// Draw placeholder images for any non-activated screens on the preview.
|
||||
/// Will release the specified device context handle if it needs to draw anything.
|
||||
/// </summary>
|
||||
public static void DrawPreviewPlaceholders(
|
||||
public static void DrawPreviewScreenPlaceholders(
|
||||
Graphics previewGraphics, IEnumerable<RectangleInfo> screenBounds)
|
||||
{
|
||||
// we can exclude the activated screen because we've already draw
|
||||
@@ -183,7 +120,7 @@ internal static class DrawingHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws screen captures from the specified desktop handle onto the target device context.
|
||||
/// Draws a screen capture from the specified desktop handle onto the target device context.
|
||||
/// </summary>
|
||||
public static void DrawPreviewScreen(
|
||||
HDC sourceHdc,
|
||||
@@ -193,7 +130,7 @@ internal static class DrawingHelper
|
||||
{
|
||||
var source = sourceBounds.ToRectangle();
|
||||
var target = targetBounds.ToRectangle();
|
||||
_ = Gdi32.StretchBlt(
|
||||
var result = Gdi32.StretchBlt(
|
||||
targetHdc,
|
||||
target.X,
|
||||
target.Y,
|
||||
@@ -204,7 +141,12 @@ internal static class DrawingHelper
|
||||
source.Y,
|
||||
source.Width,
|
||||
source.Height,
|
||||
MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY);
|
||||
Gdi32.ROP_CODE.SRCCOPY);
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -220,7 +162,7 @@ internal static class DrawingHelper
|
||||
{
|
||||
var source = sourceBounds[i].ToRectangle();
|
||||
var target = targetBounds[i].ToRectangle();
|
||||
_ = Gdi32.StretchBlt(
|
||||
var result = Gdi32.StretchBlt(
|
||||
targetHdc,
|
||||
target.X,
|
||||
target.Y,
|
||||
@@ -231,7 +173,12 @@ internal static class DrawingHelper
|
||||
source.Y,
|
||||
source.Width,
|
||||
source.Height,
|
||||
MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY);
|
||||
Gdi32.ROP_CODE.SRCCOPY);
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
92
src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs
Normal file
92
src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.Models.Layout;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class LayoutHelper
|
||||
{
|
||||
public static LayoutInfo CalculateLayoutInfo(
|
||||
LayoutConfig layoutConfig)
|
||||
{
|
||||
if (layoutConfig is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(layoutConfig));
|
||||
}
|
||||
|
||||
var builder = new LayoutInfo.Builder
|
||||
{
|
||||
LayoutConfig = layoutConfig,
|
||||
};
|
||||
|
||||
builder.ActivatedScreenBounds = layoutConfig.Screens[layoutConfig.ActivatedScreenIndex].Bounds;
|
||||
|
||||
// work out the maximum *constrained* form size
|
||||
// * can't be bigger than the activated screen
|
||||
// * can't be bigger than the max form size
|
||||
var maxFormSize = builder.ActivatedScreenBounds.Size
|
||||
.Intersect(layoutConfig.MaximumFormSize);
|
||||
|
||||
// the drawing area for screen images is inside the
|
||||
// form border and inside the preview border
|
||||
var maxDrawingSize = maxFormSize
|
||||
.Shrink(layoutConfig.FormPadding)
|
||||
.Shrink(layoutConfig.PreviewPadding);
|
||||
|
||||
// scale the virtual screen to fit inside the drawing bounds
|
||||
var scalingRatio = layoutConfig.VirtualScreenBounds.Size
|
||||
.ScaleToFitRatio(maxDrawingSize);
|
||||
|
||||
// position the drawing bounds inside the preview border
|
||||
var drawingBounds = layoutConfig.VirtualScreenBounds.Size
|
||||
.ScaleToFit(maxDrawingSize)
|
||||
.PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top);
|
||||
|
||||
// now we know the size of the drawing area we can work out the preview size
|
||||
builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding);
|
||||
|
||||
// ... and the form size
|
||||
// * center the form to the activated position, but nudge it back
|
||||
// inside the visible area of the activated screen if it falls outside
|
||||
builder.FormBounds = builder.PreviewBounds
|
||||
.Enlarge(layoutConfig.FormPadding)
|
||||
.Center(layoutConfig.ActivatedLocation)
|
||||
.Clamp(builder.ActivatedScreenBounds);
|
||||
|
||||
// now calculate the positions of each of the screen images on the preview
|
||||
builder.ScreenBounds = layoutConfig.Screens
|
||||
.Select(
|
||||
screen => screen.Bounds
|
||||
.Offset(layoutConfig.VirtualScreenBounds.Location.ToSize().Negate())
|
||||
.Scale(scalingRatio)
|
||||
.Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top))
|
||||
.ToList();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize and position the specified form.
|
||||
/// </summary>
|
||||
public static void PositionForm(
|
||||
Form form, RectangleInfo formBounds)
|
||||
{
|
||||
// note - do this in two steps rather than "this.Bounds = formBounds" as there
|
||||
// appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2,
|
||||
// where the form scaling uses either the *primary* screen scaling or the *previous*
|
||||
// screen's scaling when the form is moved to a different screen. i've got no idea
|
||||
// *why*, but the exact sequence of calls below seems to be a workaround...
|
||||
// see https://github.com/mikeclayton/FancyMouse/issues/2
|
||||
var bounds = formBounds.ToRectangle();
|
||||
form.Location = bounds.Location;
|
||||
_ = form.PointToScreen(Point.Empty);
|
||||
form.Size = bounds.Size;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Drawing;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using MouseJumpUI.Drawing.Models;
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.NativeMethods;
|
||||
using static MouseJumpUI.NativeMethods.Core;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
@@ -26,13 +29,33 @@ internal static class MouseHelper
|
||||
.Offset(desktopBounds.Location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current position of the cursor.
|
||||
/// </summary>
|
||||
public static PointInfo GetCursorPosition()
|
||||
{
|
||||
var lpPoint = new LPPOINT(new POINT(0, 0));
|
||||
var result = User32.GetCursorPos(lpPoint);
|
||||
if (!result)
|
||||
{
|
||||
throw new Win32Exception(
|
||||
Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var point = lpPoint.ToStructure();
|
||||
lpPoint.Free();
|
||||
|
||||
return new PointInfo(
|
||||
point.x, point.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the cursor to the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://github.com/mikeclayton/FancyMouse/pull/3
|
||||
/// </remarks>
|
||||
public static void JumpCursor(PointInfo location)
|
||||
public static void SetCursorPosition(PointInfo location)
|
||||
{
|
||||
// set the new cursor position *twice* - the cursor sometimes end up in
|
||||
// the wrong place if we try to cross the dead space between non-aligned
|
||||
@@ -51,8 +74,18 @@ internal static class MouseHelper
|
||||
// setting the position a second time seems to fix this and moves the
|
||||
// cursor to the expected location (b)
|
||||
var point = location.ToPoint();
|
||||
Cursor.Position = point;
|
||||
Cursor.Position = point;
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var result = User32.SetCursorPos(point.X, point.Y);
|
||||
if (!result)
|
||||
{
|
||||
throw new Win32Exception(
|
||||
Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
// temporary workaround for issue #1273
|
||||
MouseHelper.SimulateMouseMovementEvent(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,26 +95,43 @@ internal static class MouseHelper
|
||||
/// See https://github.com/microsoft/PowerToys/issues/24523
|
||||
/// https://github.com/microsoft/PowerToys/pull/24527
|
||||
/// </remarks>
|
||||
public static void SimulateMouseMovementEvent(Point location)
|
||||
public static void SimulateMouseMovementEvent(PointInfo location)
|
||||
{
|
||||
var mouseMoveInput = new NativeMethods.INPUT
|
||||
var inputs = new User32.INPUT[]
|
||||
{
|
||||
type = NativeMethods.INPUTTYPE.INPUT_MOUSE,
|
||||
data = new NativeMethods.InputUnion
|
||||
{
|
||||
mi = new NativeMethods.MOUSEINPUT
|
||||
{
|
||||
dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X),
|
||||
dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y),
|
||||
mouseData = 0,
|
||||
dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE
|
||||
| (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE,
|
||||
time = 0,
|
||||
dwExtraInfo = 0,
|
||||
},
|
||||
},
|
||||
new(
|
||||
type: User32.INPUT_TYPE.INPUT_MOUSE,
|
||||
data: new User32.INPUT.DUMMYUNIONNAME(
|
||||
mi: new User32.MOUSEINPUT(
|
||||
dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X),
|
||||
dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y),
|
||||
mouseData: 0,
|
||||
dwFlags: User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE,
|
||||
time: 0,
|
||||
dwExtraInfo: ULONG_PTR.Null))),
|
||||
};
|
||||
var inputs = new NativeMethods.INPUT[] { mouseMoveInput };
|
||||
_ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size);
|
||||
var result = User32.SendInput(
|
||||
(uint)inputs.Length,
|
||||
new User32.LPINPUT(inputs),
|
||||
User32.INPUT.Size * inputs.Length);
|
||||
if (result != inputs.Length)
|
||||
{
|
||||
throw new Win32Exception(
|
||||
Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
private static decimal CalculateAbsoluteCoordinateX(decimal x)
|
||||
{
|
||||
// If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535.
|
||||
// see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
|
||||
return (x * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CXSCREEN);
|
||||
}
|
||||
|
||||
internal static decimal CalculateAbsoluteCoordinateY(decimal y)
|
||||
{
|
||||
// If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535.
|
||||
// see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
|
||||
return (y * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CYSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
// Win32 functions required for temporary workaround for issue #1273
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int GetSystemMetrics(SystemMetric smIndex);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INPUT
|
||||
{
|
||||
internal INPUTTYPE type;
|
||||
internal InputUnion data;
|
||||
|
||||
internal static int Size
|
||||
{
|
||||
get { return Marshal.SizeOf(typeof(INPUT)); }
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct InputUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
internal MOUSEINPUT mi;
|
||||
[FieldOffset(0)]
|
||||
internal KEYBDINPUT ki;
|
||||
[FieldOffset(0)]
|
||||
internal HARDWAREINPUT hi;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct MOUSEINPUT
|
||||
{
|
||||
internal int dx;
|
||||
internal int dy;
|
||||
internal int mouseData;
|
||||
internal uint dwFlags;
|
||||
internal uint time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct KEYBDINPUT
|
||||
{
|
||||
internal short wVk;
|
||||
internal short wScan;
|
||||
internal uint dwFlags;
|
||||
internal int time;
|
||||
internal UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct HARDWAREINPUT
|
||||
{
|
||||
internal int uMsg;
|
||||
internal short wParamL;
|
||||
internal short wParamH;
|
||||
}
|
||||
|
||||
internal enum INPUTTYPE : uint
|
||||
{
|
||||
INPUT_MOUSE = 0,
|
||||
INPUT_KEYBOARD = 1,
|
||||
INPUT_HARDWARE = 2,
|
||||
}
|
||||
|
||||
internal enum MOUSE_INPUT_FLAGS : uint
|
||||
{
|
||||
MOUSEEVENTF_MOVE = 0x0001,
|
||||
MOUSEEVENTF_LEFTDOWN = 0x0002,
|
||||
MOUSEEVENTF_LEFTUP = 0x0004,
|
||||
MOUSEEVENTF_RIGHTDOWN = 0x0008,
|
||||
MOUSEEVENTF_RIGHTUP = 0x0010,
|
||||
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
|
||||
MOUSEEVENTF_MIDDLEUP = 0x0040,
|
||||
MOUSEEVENTF_XDOWN = 0x0080,
|
||||
MOUSEEVENTF_XUP = 0x0100,
|
||||
MOUSEEVENTF_WHEEL = 0x0800,
|
||||
MOUSEEVENTF_HWHEEL = 0x1000,
|
||||
MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000,
|
||||
MOUSEEVENTF_VIRTUALDESK = 0x4000,
|
||||
MOUSEEVENTF_ABSOLUTE = 0x8000,
|
||||
}
|
||||
|
||||
internal enum SystemMetric
|
||||
{
|
||||
SM_CXSCREEN = 0,
|
||||
SM_CYSCREEN = 1,
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateX(int x)
|
||||
{
|
||||
return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN);
|
||||
}
|
||||
|
||||
internal static int CalculateAbsoluteCoordinateY(int y)
|
||||
{
|
||||
return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN);
|
||||
}
|
||||
}
|
||||
94
src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs
Normal file
94
src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.Models.Screen;
|
||||
using MouseJumpUI.NativeMethods;
|
||||
using static MouseJumpUI.NativeMethods.Core;
|
||||
using static MouseJumpUI.NativeMethods.User32;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class ScreenHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Duplicates functionality available in System.Windows.Forms.SystemInformation
|
||||
/// to reduce the dependency on WinForms
|
||||
/// </summary>
|
||||
public static RectangleInfo GetVirtualScreen()
|
||||
{
|
||||
return new(
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN),
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN),
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN),
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN));
|
||||
}
|
||||
|
||||
public static IEnumerable<ScreenInfo> GetAllScreens()
|
||||
{
|
||||
// enumerate the monitors attached to the system
|
||||
var hMonitors = new List<HMONITOR>();
|
||||
var result = User32.EnumDisplayMonitors(
|
||||
HDC.Null,
|
||||
LPCRECT.Null,
|
||||
(unnamedParam1, unnamedParam2, unnamedParam3, unnamedParam4) =>
|
||||
{
|
||||
hMonitors.Add(unnamedParam1);
|
||||
return true;
|
||||
},
|
||||
LPARAM.Null);
|
||||
if (!result)
|
||||
{
|
||||
throw new Win32Exception(
|
||||
$"{nameof(User32.EnumDisplayMonitors)} failed with return code {result.Value}");
|
||||
}
|
||||
|
||||
// get detailed info about each monitor
|
||||
foreach (var hMonitor in hMonitors)
|
||||
{
|
||||
var monitorInfoPtr = new LPMONITORINFO(
|
||||
new MONITORINFO((uint)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0));
|
||||
result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr);
|
||||
if (!result)
|
||||
{
|
||||
throw new Win32Exception(
|
||||
$"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}");
|
||||
}
|
||||
|
||||
var monitorInfo = monitorInfoPtr.ToStructure();
|
||||
monitorInfoPtr.Free();
|
||||
|
||||
yield return new ScreenInfo(
|
||||
handle: hMonitor,
|
||||
primary: monitorInfo.dwFlags.HasFlag(User32.MONITOR_INFO_FLAGS.MONITORINFOF_PRIMARY),
|
||||
displayArea: new RectangleInfo(
|
||||
monitorInfo.rcMonitor.left,
|
||||
monitorInfo.rcMonitor.top,
|
||||
monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
|
||||
monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top),
|
||||
workingArea: new RectangleInfo(
|
||||
monitorInfo.rcWork.left,
|
||||
monitorInfo.rcWork.top,
|
||||
monitorInfo.rcWork.right - monitorInfo.rcWork.left,
|
||||
monitorInfo.rcWork.bottom - monitorInfo.rcWork.top));
|
||||
}
|
||||
}
|
||||
|
||||
public static HMONITOR MonitorFromPoint(
|
||||
PointInfo pt)
|
||||
{
|
||||
var hMonitor = User32.MonitorFromPoint(
|
||||
new((int)pt.X, (int)pt.Y),
|
||||
User32.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
if (hMonitor.IsNull)
|
||||
{
|
||||
throw new InvalidOperationException($"no monitor found for point {pt}");
|
||||
}
|
||||
|
||||
return hMonitor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user