From c03469fca67db1be5db2c49361ee3935718793f7 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 9 Aug 2018 16:49:08 +0100 Subject: [PATCH] Add support for DPI aware plug-ins on Windows --- .../VST/juce_VST_Wrapper.cpp | 112 +++++++++++++---- .../VST3/juce_VST3_Wrapper.cpp | 113 +++++++++++++++--- 2 files changed, 185 insertions(+), 40 deletions(-) diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index b1749ad038..a178aa2608 100644 --- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -1078,7 +1078,15 @@ public: } if (editorComp != nullptr) + { editorComp->checkVisibility(); + + #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE + if (getHostType().isWavelab()) + if (auto* peer = editorComp->getTopLevelComponent()->getPeer()) + handleSetContentScaleFactor ((float) peer->getPlatformScaleFactor()); + #endif + } } void createEditorComp() @@ -1092,10 +1100,6 @@ public: { vstEffect.flags |= Vst2::effFlagsHasEditor; editorComp.reset (new EditorCompWrapper (*this, *ed)); - - #if ! (JUCE_MAC || JUCE_IOS) - ed->setScaleFactor (editorScaleFactor); - #endif } else { @@ -1215,8 +1219,17 @@ public: // A component to hold the AudioProcessorEditor, and cope with some housekeeping // chores when it changes or repaints. struct EditorCompWrapper : public Component - { - EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor& editor) : wrapper (w) + #if ! JUCE_MAC + , public ComponentPeer::ScaleFactorListener, + public ComponentMovementWatcher + #endif + { + EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor& editor) + : + #if ! JUCE_MAC + ComponentMovementWatcher (this), + #endif + wrapper (w) { editor.setOpaque (true); editor.setVisible (true); @@ -1241,6 +1254,11 @@ public: { deleteAllChildren(); // note that we can't use a std::unique_ptr because the editor may // have been transferred to another parent which takes over ownership. + #if ! JUCE_MAC + for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) + if (auto* peer = ComponentPeer::getPeer (i)) + peer->removeScaleFactorListener (this); + #endif } void paint (Graphics&) override {} @@ -1249,10 +1267,10 @@ public: { auto b = getSizeToContainChild(); - bounds.top = 0; - bounds.left = 0; - bounds.bottom = (int16) b.getHeight(); - bounds.right = (int16) b.getWidth(); + bounds.top = 0; + bounds.left = 0; + bounds.bottom = (int16) b.getHeight(); + bounds.right = (int16) b.getWidth(); } void attachToHost (VstOpCodeArguments args) @@ -1263,6 +1281,10 @@ public: #if JUCE_WINDOWS addToDesktop (0, args.ptr); hostWindow = (HWND) args.ptr; + #if JUCE_WIN_PER_MONITOR_DPI_AWARE + // workaround for plug-ins opening on an auxiliary monitor + Timer::callAfterDelay (250, [this] { updateWindowSize (false); }); + #endif #elif JUCE_LINUX addToDesktop (0, args.ptr); hostWindow = (Window) args.ptr; @@ -1302,6 +1324,32 @@ public: return dynamic_cast (getChildComponent(0)); } + float getNativeEditorScaleFactor() const noexcept { return nativeScaleFactor; } + + #if ! JUCE_MAC + void componentMovedOrResized (bool, bool) override {} + + void componentPeerChanged() override + { + if (auto* peer = getTopLevelComponent()->getPeer()) + peer->addScaleFactorListener (this); + } + + void componentVisibilityChanged() override + { + if (auto* peer = getTopLevelComponent()->getPeer()) + nativeScaleFactorChanged (peer->getPlatformScaleFactor()); + } + + void nativeScaleFactorChanged (double newScaleFactor) override + { + nativeScaleFactor = (float) newScaleFactor; + + if (getHostType().isBitwigStudio()) + updateWindowSize (true); + } + #endif + void resized() override { if (auto* ed = getEditorComp()) @@ -1359,7 +1407,9 @@ public: shouldResizeEditor = true; #else ignoreUnused (resizeEditor); - XResizeWindow (display.display, (Window) getWindowHandle(), pos.getWidth(), pos.getHeight()); + XResizeWindow (display.display, (Window) getWindowHandle(), + static_cast (roundToInt (pos.getWidth() * nativeScaleFactor)), + static_cast (roundToInt (pos.getHeight() * nativeScaleFactor))); #endif #if JUCE_MAC @@ -1380,7 +1430,9 @@ public: if (status == (pointer_sized_int) 1 || getHostType().isAbletonLive()) { isInSizeWindow = true; - sizeWasSuccessful = (host (wrapper.getAEffect(), Vst2::audioMasterSizeWindow, newWidth, newHeight, 0, 0) != 0); + sizeWasSuccessful = (host (wrapper.getAEffect(), Vst2::audioMasterSizeWindow, + roundToInt (newWidth * nativeScaleFactor), + roundToInt (newHeight * nativeScaleFactor), 0, 0) != 0); isInSizeWindow = false; } } @@ -1478,6 +1530,8 @@ public: bool isInSizeWindow = false; bool shouldResizeEditor = true; + float nativeScaleFactor = 1.0f; + #if JUCE_MAC void* hostWindow = {}; #elif JUCE_LINUX @@ -2115,21 +2169,36 @@ private: pointer_sized_int handleSetContentScaleFactor (float scale) { - if (editorScaleFactor != scale) + #if ! JUCE_MAC + if (editorComp != nullptr) { - editorScaleFactor = scale; - - #if ! (JUCE_MAC || JUCE_IOS) - if (editorComp != nullptr) + #if JUCE_WINDOWS && ! JUCE_WIN_PER_MONITOR_DPI_AWARE + if (auto* ed = editorComp->getEditorComp()) { - if (auto* ed = editorComp->getEditorComp()) - ed->setScaleFactor (editorScaleFactor); + ed->setScaleFactor (scale); + editorComp->updateWindowSize (true); + } + #else + if (! approximatelyEqual (scale, (float) editorComp->getNativeEditorScaleFactor())) + { + editorComp->nativeScaleFactorChanged ((double) scale); - if (editorComp != nullptr) - editorComp->updateWindowSize (true); + #if JUCE_LINUX + MessageManager::callAsync ([this] { if (editorComp != nullptr) editorComp->updateWindowSize (true); }); + #else + editorComp->updateWindowSize (true); + #endif + + #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE + if (getHostType().isStudioOne()) + Timer::callAfterDelay (100, [this] { if (editorComp != nullptr) editorComp->updateWindowSize (false); }); + #endif } #endif } + #else + ignoreUnused (scale); + #endif return 1; } @@ -2185,7 +2254,6 @@ private: Vst2::ERect editorBounds; MidiBuffer midiEvents; VSTMidiEventList outgoingEvents; - float editorScaleFactor = 1.0f; LegacyAudioParametersWrapper juceParameters; diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 5c2c5ba2c6..6c09602988 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -883,7 +883,10 @@ private: if (component != nullptr) { - component->setSize (rect.getWidth(), rect.getHeight()); + auto scale = component->getNativeEditorScaleFactor(); + + component->setSize (roundToInt (rect.getWidth() / scale), + roundToInt (rect.getHeight() / scale)); if (auto* peer = component->getPeer()) peer->updateBounds(); @@ -900,7 +903,12 @@ private: { if (size != nullptr && component != nullptr) { - *size = ViewRect (0, 0, component->getWidth(), component->getHeight()); + auto scale = component->getNativeEditorScaleFactor(); + + *size = ViewRect (0, 0, + roundToInt (component->getWidth() * scale), + roundToInt (component->getHeight() * scale)); + return kResultTrue; } @@ -923,16 +931,21 @@ private: if (auto* editor = component->pluginEditor.get()) { // checkSizeConstraint - auto juceRect = editor->getLocalArea (component.get(), Rectangle::leftTopRightBottom (rectToCheck->left, rectToCheck->top, - rectToCheck->right, rectToCheck->bottom)); + auto scale = component->getNativeEditorScaleFactor(); + auto scaledRect = (Rectangle::leftTopRightBottom (rectToCheck->left, rectToCheck->top, + rectToCheck->right, rectToCheck->bottom).toFloat() / scale).toNearestInt(); + + auto juceRect = editor->getLocalArea (component.get(), scaledRect); + if (auto* constrainer = editor->getConstrainer()) { Rectangle limits (0, 0, constrainer->getMaximumWidth(), constrainer->getMaximumHeight()); constrainer->checkBounds (juceRect, editor->getBounds(), limits, false, false, false, false); juceRect = component->getLocalArea (editor, juceRect); - rectToCheck->right = rectToCheck->left + juceRect.getWidth(); - rectToCheck->bottom = rectToCheck->top + juceRect.getHeight(); + + rectToCheck->right = rectToCheck->left + roundToInt (juceRect.getWidth() * scale); + rectToCheck->bottom = rectToCheck->top + roundToInt (juceRect.getHeight() * scale); } } @@ -945,17 +958,22 @@ private: tresult PLUGIN_API setContentScaleFactor (Steinberg::IPlugViewContentScaleSupport::ScaleFactor factor) override { - #if (JUCE_MAC || JUCE_IOS) - ignoreUnused (factor); - #else - if (auto* editor = component->pluginEditor.get()) - { - editor->setScaleFactor (factor); - return kResultTrue; - } - #endif + #if ! JUCE_MAC + #if JUCE_WINDOWS && ! JUCE_WIN_PER_MONITOR_DPI_AWARE + if (auto* ed = component->pluginEditor.get()) + ed->setScaleFactor ((float) factor); + #else + if (! approximatelyEqual (component->getNativeEditorScaleFactor(), (float) factor)) + component->nativeScaleFactorChanged ((double) factor); + #endif + + component->resizeHostWindow(); + return kResultTrue; + #else + ignoreUnused (factor); return kResultFalse; + #endif } private: @@ -970,10 +988,18 @@ private: //============================================================================== struct ContentWrapperComponent : public Component + #if ! JUCE_MAC + , public ComponentPeer::ScaleFactorListener, + public ComponentMovementWatcher + #endif { ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) - : pluginEditor (plugin.createEditorIfNeeded()), - owner (editor) + : + #if ! JUCE_MAC + ComponentMovementWatcher (this), + #endif + pluginEditor (plugin.createEditorIfNeeded()), + owner (editor) { setOpaque (true); setBroughtToFrontOnMouseClick (true); @@ -1004,6 +1030,12 @@ private: PopupMenu::dismissAllActiveMenus(); pluginEditor->processor.editorBeingDeleted (pluginEditor.get()); } + + #if ! JUCE_MAC + for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) + if (auto* p = ComponentPeer::getPeer (i)) + p->removeScaleFactorListener (this); + #endif } void paint (Graphics& g) override @@ -1084,7 +1116,7 @@ private: if (owner.plugFrame != nullptr) { - ViewRect newSize (0, 0, w, h); + ViewRect newSize (0, 0, roundToInt (w * nativeScaleFactor), roundToInt (h * nativeScaleFactor)); isResizingParentToFitChild = true; owner.plugFrame->resizeView (&owner, &newSize); isResizingParentToFitChild = false; @@ -1099,6 +1131,49 @@ private: } } + float getNativeEditorScaleFactor() const noexcept { return nativeScaleFactor; } + + #if ! JUCE_MAC + void componentMovedOrResized (bool, bool) override {} + + void componentPeerChanged() override + { + if (auto* peer = getTopLevelComponent()->getPeer()) + peer->addScaleFactorListener (this); + } + + void componentVisibilityChanged() override + { + if (auto* peer = getTopLevelComponent()->getPeer()) + nativeScaleFactor = (float) peer->getPlatformScaleFactor(); + } + + void nativeScaleFactorChanged (double newScaleFactor) override + { + nativeScaleFactor = (float) newScaleFactor; + + auto host = getHostType(); + + if (host.isWavelab()) + { + Timer::callAfterDelay (250, [this] { + if (auto* peer = getPeer()) + { + peer->updateBounds(); + repaint(); + } + + resizeHostWindow(); + }); + } + else if (host.isBitwigStudio()) + { + resizeHostWindow(); + setTopLeftPosition (0, 0); + } + } + #endif + std::unique_ptr pluginEditor; private: @@ -1108,6 +1183,8 @@ private: bool isResizingChildToFitParent = false; bool isResizingParentToFitChild = false; + float nativeScaleFactor = 1.0f; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) };