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); }