From 2fcf9ebf38982c9487b334367a8b7c05c4ccb330 Mon Sep 17 00:00:00 2001 From: reuk Date: Wed, 27 Nov 2024 20:10:35 +0000 Subject: [PATCH] Windowing: Fix issue where components dragged between monitors with different scalings could detach from the mouse This bug could be observed by running the WidgetsDemo Drag+Drop pane on Windows 10, and dragging an item between two displays at different scale factors. This is issue is a regression introduced in 9817a2bb66408ba44cdf365f90f91fd33afedcc9. The regression was caused by the change in mouse position calculation. The incorrect version switched to using ClientToScreen, but the correct version used getPointFromLocalLParam. The function getPointFromLocalLParam was replaced by clientLParamToPoint in 24ab3cb6a3aa926b963614669a0ac9ab609333cf, and is restored by this commit. --- .../native/juce_Windowing_windows.cpp | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index 8af733c511..6ad1d16dd1 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -2683,22 +2683,15 @@ private: client, }; - std::optional doMouseMove (LPARAM lParam, bool isMouseDownEvent, WindowArea area) + std::optional doMouseMove (const LPARAM lParam, bool isMouseDownEvent, WindowArea area) { - auto point = getPOINTFromLParam (lParam); - - if (area == WindowArea::client) - ClientToScreen (hwnd, &point); - - const auto adjustedLParam = MAKELPARAM (point.x, point.y); - // Check if the mouse has moved since being pressed in the caption area. // If it has, then we defer to DefWindowProc to handle the mouse movement. // Allowing DefWindowProc to handle WM_NCLBUTTONDOWN directly will pause message // processing (and therefore painting) when the mouse is clicked in the caption area, // which is why we wait until the mouse is *moved* before asking the system to take over. // Letting the system handle the move is important for things like Aero Snap to work. - if (captionMouseDown.has_value() && *captionMouseDown != adjustedLParam) + if (area == WindowArea::nonclient && captionMouseDown.has_value() && *captionMouseDown != lParam) { captionMouseDown.reset(); @@ -2710,20 +2703,21 @@ private: // ModifierKeys::currentModifiers gets left in the wrong state. As a workaround, we // manually update the modifier keys after DefWindowProc exits, and update the // capture state if necessary. - const auto result = DefWindowProc (hwnd, WM_NCLBUTTONDOWN, HTCAPTION, adjustedLParam); + const auto result = DefWindowProc (hwnd, WM_NCLBUTTONDOWN, HTCAPTION, lParam); getMouseModifiers(); releaseCaptureIfNecessary(); return result; } - const auto position = getLocalPointFromScreenLParam (adjustedLParam); - ModifierKeys modsToSend (ModifierKeys::currentModifiers); // this will be handled by WM_TOUCH if (isTouchEvent() || areOtherTouchSourcesActive()) return {}; + const auto position = area == WindowArea::client ? getPointFromLocalLParam (lParam) + : getLocalPointFromScreenLParam (lParam); + if (! isMouseOver) { isMouseOver = true; @@ -2808,7 +2802,7 @@ private: isDragging = true; - doMouseEvent (clientLparamToPoint (lParam), MouseInputSource::defaultPressure); + doMouseEvent (getPointFromLocalLParam (lParam), MouseInputSource::defaultPressure); } } @@ -3726,11 +3720,21 @@ private: return globalToLocal (convertPhysicalScreenPointToLogical (globalPos, hwnd).toFloat()); } - Point clientLparamToPoint (LPARAM lParam) + Point getPointFromLocalLParam (LPARAM lParam) noexcept { - auto point = getPOINTFromLParam (lParam); - ClientToScreen (hwnd, &point); - return getLocalPointFromScreenLParam (MAKELPARAM (point.x, point.y)); + const auto p = D2DUtilities::toPoint (getPOINTFromLParam (lParam)); + + if (! isPerMonitorDPIAwareWindow (hwnd)) + return p.toFloat(); + + // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the + // physical screen position and then convert this to local logical coordinates + auto r = getWindowScreenRect (hwnd); + const auto windowBorder = findPhysicalBorderSize().value_or (BorderSize{}); + const auto offset = p + + Point { (int) r.left, (int) r.top } + + Point { windowBorder.getLeft(), windowBorder.getTop() }; + return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (offset).toFloat()); } Point getCurrentMousePos() noexcept @@ -3978,7 +3982,7 @@ private: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - doMouseUp (clientLparamToPoint (lParam), wParam); + doMouseUp (getPointFromLocalLParam (lParam), wParam); return 0; case WM_POINTERWHEEL: