From e210b295ce6a2342121e261ef1dbdb50d89bdf1b Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 17 Jan 2022 13:02:16 +0000 Subject: [PATCH] Windows: Fix issue where windows could be given incorrect size for their current scale This issue affected windows with JUCE titlebars. When dragging the window back and forth between two displays with different DPI settings, the window could sometimes end up with the wrong size. It looks like setting new window bounds inside the WM_WINDOWPOSCHANGED message was occasionally causing the system to lose track of the correct scale for the window. As a byproduct of this change, JUCE should no longer receive reentrant calls to handleDPIChanging. --- .../native/juce_win32_Windowing.cpp | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index c58995d0cc..089386a6ff 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -1583,6 +1583,13 @@ public: void setBounds (const Rectangle& bounds, bool isNowFullScreen) override { + // If we try to set new bounds while handling an existing position change, + // Windows may get confused about our current scale and size. + // This can happen when moving a window between displays, because the mouse-move + // generator in handlePositionChanged can cause the window to move again. + if (inHandlePositionChanged) + return; + const ScopedValueSetter scope (shouldIgnoreModalDismiss, true); fullScreen = isNowFullScreen; @@ -1608,7 +1615,7 @@ public: if (! hasMoved) flags |= SWP_NOMOVE; if (! hasResized) flags |= SWP_NOSIZE; - setWindowPos (hwnd, newBounds, flags, numInDpiChange == 0); + setWindowPos (hwnd, newBounds, flags, ! inDpiChange); if (hasResized && isValidPeer (this)) { @@ -2125,7 +2132,7 @@ private: #endif double scaleFactor = 1.0; - int numInDpiChange = 0; + bool inDpiChange = 0, inHandlePositionChanged = 0; bool isAccessibilityActive = false; @@ -3448,6 +3455,8 @@ private: if (contains (pos.roundToInt(), false)) { + const ScopedValueSetter scope (inHandlePositionChanged, true); + if (! areOtherTouchSourcesActive()) doMouseEvent (pos, MouseInputSource::invalidPressure); @@ -3477,15 +3486,27 @@ private: scaleFactor = newScale; { - const ScopedValueSetter setter (numInDpiChange, numInDpiChange + 1); - setBounds (windowBorder.subtractedFrom (convertPhysicalScreenRectangleToLogical (rectangleFromRECT (newRect), hwnd)), fullScreen); + const ScopedValueSetter setter (inDpiChange, true); + SetWindowPos (hwnd, + nullptr, + newRect.left, + newRect.top, + newRect.right - newRect.left, + newRect.bottom - newRect.top, + SWP_NOZORDER | SWP_NOACTIVATE); } // This is to handle reentrancy. If responding to a DPI change triggers further DPI changes, // we should only notify listeners and resize windows once all of the DPI changes have // resolved. - if (numInDpiChange != 0) + if (inDpiChange) + { + // Danger! Re-entrant call to handleDPIChanging. + // Please report this issue on the JUCE forum, along with instructions + // so that a JUCE developer can reproduce the issue. + jassertfalse; return 0; + } updateShadower(); InvalidateRect (hwnd, nullptr, FALSE);