| @@ -111,6 +111,13 @@ | |||||
| #define JUCE_USE_XCURSOR 1 | #define JUCE_USE_XCURSOR 1 | ||||
| #endif | #endif | ||||
| /** Config: JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| Enables per-monitor DPI awareness on Windows 8.1 and above. | |||||
| */ | |||||
| #ifndef JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| #define JUCE_WIN_PER_MONITOR_DPI_AWARE 1 | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| @@ -0,0 +1,56 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| JUCE is an open source library subject to commercial or open-source | |||||
| licensing. | |||||
| By using JUCE, you agree to the terms of both the JUCE 5 End-User License | |||||
| Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||||
| 27th April 2017). | |||||
| End User License Agreement: www.juce.com/juce-5-licence | |||||
| Privacy Policy: www.juce.com/juce-5-privacy-policy | |||||
| Or: You may also use this code under the terms of the GPL v3 (see | |||||
| www.gnu.org/licenses). | |||||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||||
| DISCLAIMED. | |||||
| ============================================================================== | |||||
| */ | |||||
| namespace juce | |||||
| { | |||||
| #if (JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE) || DOXYGEN | |||||
| //============================================================================== | |||||
| /** | |||||
| A Windows-specific class that temporarily sets the DPI awareness context of | |||||
| the current thread to be DPI unaware and resets it to the previous context | |||||
| when it goes out of scope. | |||||
| If you create one of these before creating a top-level window, the window | |||||
| will be DPI unaware and bitmap strectched by the OS on a display with >100% | |||||
| scaling. | |||||
| You shouldn't use this unless you really know what you are doing and | |||||
| are dealing with native HWNDs. | |||||
| */ | |||||
| class JUCE_API ScopedDPIAwarenessDisabler | |||||
| { | |||||
| public: | |||||
| ScopedDPIAwarenessDisabler(); | |||||
| ~ScopedDPIAwarenessDisabler(); | |||||
| private: | |||||
| void* previousContext = nullptr; | |||||
| }; | |||||
| #endif | |||||
| } // namespace juce | |||||
| @@ -88,6 +88,7 @@ | |||||
| #include "embedding/juce_NSViewComponent.h" | #include "embedding/juce_NSViewComponent.h" | ||||
| #include "embedding/juce_UIViewComponent.h" | #include "embedding/juce_UIViewComponent.h" | ||||
| #include "embedding/juce_XEmbedComponent.h" | #include "embedding/juce_XEmbedComponent.h" | ||||
| #include "embedding/juce_ScopedDPIAwarenessDisabler.h" | |||||
| #include "misc/juce_AppleRemote.h" | #include "misc/juce_AppleRemote.h" | ||||
| #include "misc/juce_BubbleMessageComponent.h" | #include "misc/juce_BubbleMessageComponent.h" | ||||
| #include "misc/juce_ColourSelector.h" | #include "misc/juce_ColourSelector.h" | ||||
| @@ -231,6 +231,9 @@ namespace ActiveXHelpers | |||||
| //============================================================================== | //============================================================================== | ||||
| class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher | class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| , public ComponentPeer::ScaleFactorListener | |||||
| #endif | |||||
| { | { | ||||
| public: | public: | ||||
| Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp) | Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp) | ||||
| @@ -251,12 +254,26 @@ public: | |||||
| clientSite->Release(); | clientSite->Release(); | ||||
| storage->Release(); | storage->Release(); | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) | |||||
| if (auto* peer = ComponentPeer::getPeer (i)) | |||||
| peer->removeScaleFactorListener (this); | |||||
| #endif | |||||
| } | } | ||||
| void setControlBounds (Rectangle<int> newBounds) const | void setControlBounds (Rectangle<int> newBounds) const | ||||
| { | { | ||||
| if (controlHWND != 0) | if (controlHWND != 0) | ||||
| { | |||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| if (auto* peer = owner.getTopLevelComponent()->getPeer()) | |||||
| newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt(); | |||||
| #endif | |||||
| MoveWindow (controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE); | MoveWindow (controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE); | ||||
| } | |||||
| } | } | ||||
| void setControlVisible (bool shouldBeVisible) const | void setControlVisible (bool shouldBeVisible) const | ||||
| @@ -269,12 +286,17 @@ public: | |||||
| void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override | void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override | ||||
| { | { | ||||
| if (auto* peer = owner.getTopLevelComponent()->getPeer()) | if (auto* peer = owner.getTopLevelComponent()->getPeer()) | ||||
| setControlBounds (peer->getAreaCoveredBy(owner)); | |||||
| setControlBounds (peer->getAreaCoveredBy (owner)); | |||||
| } | } | ||||
| void componentPeerChanged() override | void componentPeerChanged() override | ||||
| { | { | ||||
| componentMovedOrResized (true, true); | componentMovedOrResized (true, true); | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| if (auto* peer = owner.getTopLevelComponent()->getPeer()) | |||||
| peer->addScaleFactorListener (this); | |||||
| #endif | |||||
| } | } | ||||
| void componentVisibilityChanged() override | void componentVisibilityChanged() override | ||||
| @@ -283,6 +305,13 @@ public: | |||||
| componentPeerChanged(); | componentPeerChanged(); | ||||
| } | } | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| void nativeScaleFactorChanged (double /*newScaleFactor*/) override | |||||
| { | |||||
| componentMovedOrResized (true, true); | |||||
| } | |||||
| #endif | |||||
| // intercepts events going to an activeX control, so we can sneakily use the mouse events | // intercepts events going to an activeX control, so we can sneakily use the mouse events | ||||
| static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| @@ -31,6 +31,9 @@ extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* | |||||
| //============================================================================== | //============================================================================== | ||||
| class OpenGLContext::NativeContext | class OpenGLContext::NativeContext | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| : public ComponentPeer::ScaleFactorListener | |||||
| #endif | |||||
| { | { | ||||
| public: | public: | ||||
| NativeContext (Component& component, | NativeContext (Component& component, | ||||
| @@ -87,6 +90,12 @@ public: | |||||
| { | { | ||||
| deleteRenderContext(); | deleteRenderContext(); | ||||
| releaseDC(); | releaseDC(); | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) | |||||
| if (auto* peer = ComponentPeer::getPeer (i)) | |||||
| peer->removeScaleFactorListener (this); | |||||
| #endif | |||||
| } | } | ||||
| bool initialiseOnRenderThread (OpenGLContext& c) | bool initialiseOnRenderThread (OpenGLContext& c) | ||||
| @@ -117,9 +126,17 @@ public: | |||||
| void updateWindowPosition (Rectangle<int> bounds) | void updateWindowPosition (Rectangle<int> bounds) | ||||
| { | { | ||||
| if (nativeWindow != nullptr) | if (nativeWindow != nullptr) | ||||
| { | |||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| if (safeComponent != nullptr) | |||||
| if (auto* peer = safeComponent->getTopLevelComponent()->getPeer()) | |||||
| bounds = (bounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt(); | |||||
| #endif | |||||
| SetWindowPos ((HWND) nativeWindow->getNativeHandle(), 0, | SetWindowPos ((HWND) nativeWindow->getNativeHandle(), 0, | ||||
| bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), | bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), | ||||
| SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); | ||||
| } | |||||
| } | } | ||||
| bool createdOk() const noexcept { return getRawContext() != nullptr; } | bool createdOk() const noexcept { return getRawContext() != nullptr; } | ||||
| @@ -134,6 +151,15 @@ public: | |||||
| struct Locker { Locker (NativeContext&) {} }; | struct Locker { Locker (NativeContext&) {} }; | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| void nativeScaleFactorChanged (double /*newScaleFactor*/) override | |||||
| { | |||||
| if (safeComponent != nullptr) | |||||
| if (auto peer = safeComponent->getTopLevelComponent()->getPeer()) | |||||
| updateWindowPosition (peer->getAreaCoveredBy (*safeComponent)); | |||||
| } | |||||
| #endif | |||||
| private: | private: | ||||
| struct DummyComponent : public Component | struct DummyComponent : public Component | ||||
| { | { | ||||
| @@ -150,6 +176,9 @@ private: | |||||
| HGLRC renderContext; | HGLRC renderContext; | ||||
| HDC dc; | HDC dc; | ||||
| OpenGLContext* context = {}; | OpenGLContext* context = {}; | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| Component::SafePointer<Component> safeComponent; | |||||
| #endif | |||||
| #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \ | #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \ | ||||
| typedef returnType (__stdcall *type_ ## name) params; type_ ## name name; | typedef returnType (__stdcall *type_ ## name) params; type_ ## name name; | ||||
| @@ -174,8 +203,15 @@ private: | |||||
| nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle())); | nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle())); | ||||
| if (auto* peer = topComp->getPeer()) | if (auto* peer = topComp->getPeer()) | ||||
| { | |||||
| updateWindowPosition (peer->getAreaCoveredBy (component)); | updateWindowPosition (peer->getAreaCoveredBy (component)); | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| peer->addScaleFactorListener (this); | |||||
| safeComponent = Component::SafePointer<Component> (&component); | |||||
| #endif | |||||
| } | |||||
| nativeWindow->setVisible (true); | nativeWindow->setVisible (true); | ||||
| dc = GetDC ((HWND) nativeWindow->getNativeHandle()); | dc = GetDC ((HWND) nativeWindow->getNativeHandle()); | ||||
| } | } | ||||
| @@ -159,6 +159,9 @@ namespace VideoRenderers | |||||
| //============================================================================== | //============================================================================== | ||||
| struct VideoComponent::Pimpl : public Component | struct VideoComponent::Pimpl : public Component | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| , public ComponentPeer::ScaleFactorListener | |||||
| #endif | |||||
| { | { | ||||
| Pimpl (VideoComponent& ownerToUse, bool) | Pimpl (VideoComponent& ownerToUse, bool) | ||||
| : owner (ownerToUse), | : owner (ownerToUse), | ||||
| @@ -174,6 +177,12 @@ struct VideoComponent::Pimpl : public Component | |||||
| close(); | close(); | ||||
| context = nullptr; | context = nullptr; | ||||
| componentWatcher = nullptr; | componentWatcher = nullptr; | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) | |||||
| if (auto* peer = ComponentPeer::getPeer (i)) | |||||
| peer->removeScaleFactorListener (this); | |||||
| #endif | |||||
| } | } | ||||
| Result loadFromString (const String& fileOrURLPath) | Result loadFromString (const String& fileOrURLPath) | ||||
| @@ -300,7 +309,7 @@ struct VideoComponent::Pimpl : public Component | |||||
| if (getWidth() > 0 && getHeight() > 0) | if (getWidth() > 0 && getHeight() > 0) | ||||
| if (auto* peer = getTopLevelComponent()->getPeer()) | if (auto* peer = getTopLevelComponent()->getPeer()) | ||||
| context->updateWindowPosition (peer->getAreaCoveredBy (*this)); | |||||
| context->updateWindowPosition ((peer->getAreaCoveredBy (*this).toDouble() * peer->getPlatformScaleFactor()).toNearestInt()); | |||||
| } | } | ||||
| void updateContextVisibility() | void updateContextVisibility() | ||||
| @@ -332,6 +341,14 @@ struct VideoComponent::Pimpl : public Component | |||||
| owner.onErrorOccurred (errorMessage); | owner.onErrorOccurred (errorMessage); | ||||
| } | } | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| void nativeScaleFactorChanged (double /*newScaleFactor*/) override | |||||
| { | |||||
| if (videoLoaded) | |||||
| updateContextPosition(); | |||||
| } | |||||
| #endif | |||||
| File currentFile; | File currentFile; | ||||
| URL currentURL; | URL currentURL; | ||||
| @@ -755,6 +772,10 @@ private: | |||||
| hwnd = nativeWindow->hwnd; | hwnd = nativeWindow->hwnd; | ||||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
| topLevelPeer->addScaleFactorListener (&component); | |||||
| #endif | |||||
| if (hwnd != 0) | if (hwnd != 0) | ||||
| { | { | ||||
| hdc = GetDC (hwnd); | hdc = GetDC (hwnd); | ||||