diff --git a/src/DesktopMagic/Helpers/ScreenUtilities.cs b/src/DesktopMagic/Helpers/ScreenUtilities.cs
index 0e8bf80..5a6a7c0 100644
--- a/src/DesktopMagic/Helpers/ScreenUtilities.cs
+++ b/src/DesktopMagic/Helpers/ScreenUtilities.cs
@@ -122,34 +122,49 @@ public static class ScreenUtilities
}
///
- /// Clamps an absolute WPF position (DIPs) so that the window (of the given size)
- /// stays within the screen bounds. This prevents widgets from being moved to another screen.
+ /// Window hook that keeps a window inside the given screen bounds.
+ /// fires only after a move has already happened, and the system move loop overrides any correction
+ /// made there on the next mouse move, so the proposed position is adjusted in WM_WINDOWPOSCHANGING
+ /// instead, which every move and resize goes through. The position and the bounds are both in this
+ /// process' screen coordinates, so no DIP conversion is needed.
///
- public static Point ClampToScreenBounds(Point topLeft, Size size, System.Drawing.Rectangle bounds)
+ public static IntPtr ClampToScreenHook(IntPtr hwnd, int msg, IntPtr lParam, System.Drawing.Rectangle bounds)
{
- Rect dips = GetScreenDips(bounds);
- double x = topLeft.X;
- double y = topLeft.Y;
-
- if (size.Width <= dips.Width)
+ if (msg != NativeMethods.WM_WINDOWPOSCHANGING || bounds.Width == 0 || bounds.Height == 0)
{
- x = Math.Clamp(x, dips.Left, dips.Right - size.Width);
- }
- else
- {
- x = dips.Left;
+ return IntPtr.Zero;
}
- if (size.Height <= dips.Height)
+ NativeMethods.WINDOWPOS pos = Marshal.PtrToStructure(lParam);
+ if ((pos.flags & NativeMethods.SWP_NOMOVE) != 0)
{
- y = Math.Clamp(y, dips.Top, dips.Bottom - size.Height);
- }
- else
- {
- y = dips.Top;
+ return IntPtr.Zero;
}
- return new Point(x, y);
+ int width = pos.cx;
+ int height = pos.cy;
+ if ((pos.flags & NativeMethods.SWP_NOSIZE) != 0)
+ {
+ if (!W32.GetWindowRect(hwnd, out W32.RECT current))
+ {
+ return IntPtr.Zero;
+ }
+
+ width = current.Width;
+ height = current.Height;
+ }
+
+ int x = width <= bounds.Width ? Math.Clamp(pos.x, bounds.Left, bounds.Right - width) : bounds.Left;
+ int y = height <= bounds.Height ? Math.Clamp(pos.y, bounds.Top, bounds.Bottom - height) : bounds.Top;
+
+ if (x != pos.x || y != pos.y)
+ {
+ pos.x = x;
+ pos.y = y;
+ Marshal.StructureToPtr(pos, lParam, false);
+ }
+
+ return IntPtr.Zero;
}
///
diff --git a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
index 899f851..82e5002 100644
--- a/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
+++ b/src/DesktopMagic/Plugins/PluginWindow.xaml.cs
@@ -421,6 +421,9 @@ public partial class PluginWindow : Window, IPluginWindow
WindowInteropHelper helper = new(this);
_ = WindowPos.SetWindowLong(helper.Handle, WindowPos.GWL_EXSTYLE,
WindowPos.GetWindowLong(helper.Handle, WindowPos.GWL_EXSTYLE) | WindowPos.WS_EX_NOACTIVATE);
+
+ HwndSource.FromHwnd(helper.Handle)?.AddHook((IntPtr hwnd, int msg, IntPtr _, IntPtr lParam, ref bool _) =>
+ ScreenUtilities.ClampToScreenHook(hwnd, msg, lParam, screenBounds));
}
private void UpdateImageFromBitmap(Bitmap bitmap, BitmapScalingMode scalingMode)
@@ -1394,17 +1397,6 @@ public partial class PluginWindow : Window, IPluginWindow
return;
}
- System.Windows.Point topLeft = new(Left, Top);
- System.Windows.Point clamped = ScreenUtilities.ClampToScreenBounds(topLeft, new System.Windows.Size(ActualWidth, ActualHeight), screenBounds);
-
- if (clamped != topLeft)
- {
- isUpdatingPosition = true;
- Left = clamped.X;
- Top = clamped.Y;
- isUpdatingPosition = false;
- }
-
settings.Position = ScreenUtilities.PositionToPercent(new System.Windows.Point(Left, Top), screenBounds);
RefreshWindowPixelSize();
}
diff --git a/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs b/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs
index 2987563..78c2a48 100644
--- a/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs
+++ b/src/DesktopMagic/Plugins/WebPluginWindow.xaml.cs
@@ -218,6 +218,9 @@ public partial class WebPluginWindow : Window, IPluginWindow
WindowInteropHelper helper = new(this);
_ = WindowPos.SetWindowLong(helper.Handle, WindowPos.GWL_EXSTYLE,
WindowPos.GetWindowLong(helper.Handle, WindowPos.GWL_EXSTYLE) | WindowPos.WS_EX_NOACTIVATE);
+
+ HwndSource.FromHwnd(helper.Handle)?.AddHook((IntPtr hwnd, int msg, IntPtr _, IntPtr lParam, ref bool _) =>
+ ScreenUtilities.ClampToScreenHook(hwnd, msg, lParam, screenBounds));
}
private async void Window_ContentRendered(object? sender, EventArgs e)
@@ -436,17 +439,6 @@ public partial class WebPluginWindow : Window, IPluginWindow
return;
}
- Point topLeft = new(Left, Top);
- Point clamped = ScreenUtilities.ClampToScreenBounds(topLeft, new Size(ActualWidth, ActualHeight), screenBounds);
-
- if (clamped != topLeft)
- {
- isUpdatingPosition = true;
- Left = clamped.X;
- Top = clamped.Y;
- isUpdatingPosition = false;
- }
-
settings.Position = ScreenUtilities.PositionToPercent(new Point(Left, Top), screenBounds);
}