Skip to content

Commit

Permalink
Mouse Jump] - reorganise Helper classes / main form code (microsoft#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeclayton committed Apr 13, 2023
1 parent 6b7788b commit c474a9e
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 165 deletions.
123 changes: 35 additions & 88 deletions src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,95 +7,16 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using MouseJumpUI.Drawing.Models;
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)
Expand Down Expand Up @@ -127,14 +48,24 @@ public static void EnsureDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desk
if (desktopHdc.IsNull)
{
desktopHdc = User32.GetWindowDC(desktopHwnd);
if (desktopHdc.IsNull)
{
throw new InvalidOperationException(
$"{nameof(User32.GetWindowDC)} returned null");
}
}
}

public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
{
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;
Expand All @@ -143,14 +74,20 @@ public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC deskto

/// <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, 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}");
}
}
}

Expand All @@ -170,7 +107,7 @@ public static void FreePreviewDeviceContext(Graphics previewGraphics, ref HDC pr
/// 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
Expand All @@ -183,7 +120,7 @@ public static void DrawPreviewPlaceholders(
}

/// <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,
Expand All @@ -193,7 +130,7 @@ public static void DrawPreviewScreen(
{
var source = sourceBounds.ToRectangle();
var target = targetBounds.ToRectangle();
_ = Gdi32.StretchBlt(
var result = Gdi32.StretchBlt(
targetHdc,
target.X,
target.Y,
Expand All @@ -205,6 +142,11 @@ public static void DrawPreviewScreen(
source.Width,
source.Height,
Gdi32.ROP_CODE.SRCCOPY);
if (!result)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
}
}

/// <summary>
Expand All @@ -220,7 +162,7 @@ public static void DrawPreviewScreens(
{
var source = sourceBounds[i].ToRectangle();
var target = targetBounds[i].ToRectangle();
_ = Gdi32.StretchBlt(
var result = Gdi32.StretchBlt(
targetHdc,
target.X,
target.Y,
Expand All @@ -232,6 +174,11 @@ public static void DrawPreviewScreens(
source.Width,
source.Height,
Gdi32.ROP_CODE.SRCCOPY);
if (!result)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
}
}
}
}
92 changes: 92 additions & 0 deletions src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs
Original file line number Diff line number Diff line change
@@ -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.ScreenBounds[layoutConfig.ActivatedScreenIndex];

// 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.ScreenBounds
.Select(
screen => screen
.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;
}
}
9 changes: 4 additions & 5 deletions src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +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;
using System.ComponentModel;
using System.Drawing;
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;

Expand Down Expand Up @@ -66,7 +65,7 @@ public static void JumpCursor(PointInfo location)
/// 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 inputs = new User32.INPUT[]
{
Expand All @@ -79,7 +78,7 @@ public static void SimulateMouseMovementEvent(Point location)
mouseData: 0,
dwFlags: User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE,
time: 0,
dwExtraInfo: UIntPtr.Zero))),
dwExtraInfo: ULONG_PTR.Null))),
};
var result = User32.SendInput(
(uint)inputs.Length,
Expand Down
Loading

0 comments on commit c474a9e

Please sign in to comment.