| @@ -111,6 +111,13 @@ | |||
| #define JUCE_USE_XCURSOR 1 | |||
| #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 | |||
| { | |||
| @@ -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_UIViewComponent.h" | |||
| #include "embedding/juce_XEmbedComponent.h" | |||
| #include "embedding/juce_ScopedDPIAwarenessDisabler.h" | |||
| #include "misc/juce_AppleRemote.h" | |||
| #include "misc/juce_BubbleMessageComponent.h" | |||
| #include "misc/juce_ColourSelector.h" | |||
| @@ -231,6 +231,9 @@ namespace ActiveXHelpers | |||
| //============================================================================== | |||
| class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| , public ComponentPeer::ScaleFactorListener | |||
| #endif | |||
| { | |||
| public: | |||
| Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp) | |||
| @@ -251,12 +254,26 @@ public: | |||
| clientSite->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 | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| void setControlVisible (bool shouldBeVisible) const | |||
| @@ -269,12 +286,17 @@ public: | |||
| void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override | |||
| { | |||
| if (auto* peer = owner.getTopLevelComponent()->getPeer()) | |||
| setControlBounds (peer->getAreaCoveredBy(owner)); | |||
| setControlBounds (peer->getAreaCoveredBy (owner)); | |||
| } | |||
| void componentPeerChanged() override | |||
| { | |||
| componentMovedOrResized (true, true); | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| if (auto* peer = owner.getTopLevelComponent()->getPeer()) | |||
| peer->addScaleFactorListener (this); | |||
| #endif | |||
| } | |||
| void componentVisibilityChanged() override | |||
| @@ -283,6 +305,13 @@ public: | |||
| 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 | |||
| static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| @@ -31,6 +31,9 @@ extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* | |||
| //============================================================================== | |||
| class OpenGLContext::NativeContext | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| : public ComponentPeer::ScaleFactorListener | |||
| #endif | |||
| { | |||
| public: | |||
| NativeContext (Component& component, | |||
| @@ -87,6 +90,12 @@ public: | |||
| { | |||
| deleteRenderContext(); | |||
| 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) | |||
| @@ -117,9 +126,17 @@ public: | |||
| void updateWindowPosition (Rectangle<int> bounds) | |||
| { | |||
| 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, | |||
| bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), | |||
| SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); | |||
| } | |||
| } | |||
| bool createdOk() const noexcept { return getRawContext() != nullptr; } | |||
| @@ -134,6 +151,15 @@ public: | |||
| 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: | |||
| struct DummyComponent : public Component | |||
| { | |||
| @@ -150,6 +176,9 @@ private: | |||
| HGLRC renderContext; | |||
| HDC dc; | |||
| OpenGLContext* context = {}; | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| Component::SafePointer<Component> safeComponent; | |||
| #endif | |||
| #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \ | |||
| typedef returnType (__stdcall *type_ ## name) params; type_ ## name name; | |||
| @@ -174,8 +203,15 @@ private: | |||
| nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle())); | |||
| if (auto* peer = topComp->getPeer()) | |||
| { | |||
| updateWindowPosition (peer->getAreaCoveredBy (component)); | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| peer->addScaleFactorListener (this); | |||
| safeComponent = Component::SafePointer<Component> (&component); | |||
| #endif | |||
| } | |||
| nativeWindow->setVisible (true); | |||
| dc = GetDC ((HWND) nativeWindow->getNativeHandle()); | |||
| } | |||
| @@ -159,6 +159,9 @@ namespace VideoRenderers | |||
| //============================================================================== | |||
| struct VideoComponent::Pimpl : public Component | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| , public ComponentPeer::ScaleFactorListener | |||
| #endif | |||
| { | |||
| Pimpl (VideoComponent& ownerToUse, bool) | |||
| : owner (ownerToUse), | |||
| @@ -174,6 +177,12 @@ struct VideoComponent::Pimpl : public Component | |||
| close(); | |||
| context = 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) | |||
| @@ -300,7 +309,7 @@ struct VideoComponent::Pimpl : public Component | |||
| if (getWidth() > 0 && getHeight() > 0) | |||
| if (auto* peer = getTopLevelComponent()->getPeer()) | |||
| context->updateWindowPosition (peer->getAreaCoveredBy (*this)); | |||
| context->updateWindowPosition ((peer->getAreaCoveredBy (*this).toDouble() * peer->getPlatformScaleFactor()).toNearestInt()); | |||
| } | |||
| void updateContextVisibility() | |||
| @@ -332,6 +341,14 @@ struct VideoComponent::Pimpl : public Component | |||
| owner.onErrorOccurred (errorMessage); | |||
| } | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| void nativeScaleFactorChanged (double /*newScaleFactor*/) override | |||
| { | |||
| if (videoLoaded) | |||
| updateContextPosition(); | |||
| } | |||
| #endif | |||
| File currentFile; | |||
| URL currentURL; | |||
| @@ -755,6 +772,10 @@ private: | |||
| hwnd = nativeWindow->hwnd; | |||
| #if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||
| topLevelPeer->addScaleFactorListener (&component); | |||
| #endif | |||
| if (hwnd != 0) | |||
| { | |||
| hdc = GetDC (hwnd); | |||