Skip to content

Commit

Permalink
[Mouse Jump] - replace use of System.Windows.Forms.Cursor with Native…
Browse files Browse the repository at this point in the history
… Methods (microsoft#25482)
  • Loading branch information
mikeclayton committed Apr 13, 2023
1 parent df70a09 commit d0489ae
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 8 deletions.
36 changes: 33 additions & 3 deletions src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,33 @@ public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo prev
.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
Expand All @@ -54,8 +74,18 @@ public static void JumpCursor(PointInfo location)
// 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>
Expand Down
7 changes: 2 additions & 5 deletions src/modules/MouseUtils/MouseJumpUI/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,7 @@ private void Thumbnail_Click(object sender, EventArgs e)
new SizeInfo(this.Thumbnail.Size),
virtualScreen);
Logger.LogInfo($"scaled location = {scaledLocation}");
MouseHelper.JumpCursor(scaledLocation);

// Simulate mouse input for handlers that won't just catch the Cursor change
MouseHelper.SimulateMouseMovementEvent(scaledLocation);
MouseHelper.SetCursorPosition(scaledLocation);
Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent());
}

Expand Down Expand Up @@ -106,7 +103,7 @@ private static LayoutInfo GetLayoutInfo(MainForm form)
}

// collect together some values that we need for calculating layout
var activatedLocation = new PointInfo(Cursor.Position);
var activatedLocation = MouseHelper.GetCursorPosition();
var activatedScreenHandle = ScreenHelper.MonitorFromPoint(activatedLocation);
var activatedScreenIndex = screens
.Single(item => item.Screen.Handle == activatedScreenHandle.Value)
Expand Down
53 changes: 53 additions & 0 deletions src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;

namespace MouseJumpUI.NativeMethods;

internal static partial class Core
{
internal readonly struct LPPOINT
{
public static readonly LPPOINT Null = new(IntPtr.Zero);

public readonly IntPtr Value;

public LPPOINT(IntPtr value)
{
this.Value = value;
}

public LPPOINT(POINT value)
{
this.Value = LPPOINT.ToPtr(value);
}

private static IntPtr ToPtr(POINT value)
{
var ptr = Marshal.AllocHGlobal(POINT.Size);
Marshal.StructureToPtr(value, ptr, false);
return ptr;
}

public POINT ToStructure()
{
return Marshal.PtrToStructure<POINT>(this.Value);
}

public void Free()
{
Marshal.FreeHGlobal(this.Value);
}

public static implicit operator IntPtr(LPPOINT value) => value.Value;

public static implicit operator LPPOINT(IntPtr value) => new(value);

public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using static MouseJumpUI.NativeMethods.Core;

namespace MouseJumpUI.NativeMethods;

internal static partial class User32
{
/// <summary>
/// Retrieves the position of the mouse cursor, in screen coordinates.
/// </summary>
/// <returns>
/// Returns nonzero if successful or zero otherwise.
/// To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
/// </remarks>
[LibraryImport(Libraries.User32, SetLastError = true)]
internal static partial BOOL GetCursorPos(
LPPOINT lpPoint);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using static MouseJumpUI.NativeMethods.Core;

namespace MouseJumpUI.NativeMethods;

internal static partial class User32
{
/// <summary>
/// Moves the cursor to the specified screen coordinates. If the new coordinates are not within
/// the screen rectangle set by the most recent ClipCursor function call, the system automatically
/// adjusts the coordinates so that the cursor stays within the rectangle.
/// </summary>
/// <returns>
/// Returns nonzero if successful or zero otherwise.
/// To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
/// </remarks>
[LibraryImport(Libraries.User32, SetLastError = true)]
internal static partial BOOL SetCursorPos(
int X,
int Y);
}

0 comments on commit d0489ae

Please sign in to comment.