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 a727a92bdb..59ea7348bb 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -97,7 +97,7 @@ using namespace Steinberg; #endif #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - extern JUCE_API double getScaleFactorForWindow (HWND); + double getScaleFactorForWindow (HWND); #endif //============================================================================== diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 0b7b50a7f3..16f10392f4 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -1333,7 +1333,6 @@ private: //============================================================================== struct VST3PluginWindow : public AudioProcessorEditor, private ComponentMovementWatcher, - private ComponentPeer::ScaleFactorListener, private IPlugFrame { VST3PluginWindow (AudioPluginInstance* owner, IPlugView* pluginView) @@ -1360,8 +1359,6 @@ struct VST3PluginWindow : public AudioProcessorEditor, if (scaleInterface != nullptr) scaleInterface->release(); - removeScaleFactorListener(); - #if JUCE_LINUX || JUCE_BSD embeddedComponent.removeClient(); #endif @@ -1421,16 +1418,19 @@ struct VST3PluginWindow : public AudioProcessorEditor, private: //============================================================================== - void componentPeerChanged() override + void componentPeerChanged() override {} + + /* Convert from the component's coordinate system to the hosted VST3's coordinate system. */ + ViewRect componentToVST3Rect (Rectangle r) const { - removeScaleFactorListener(); - currentPeer = getTopLevelComponent()->getPeer(); + const auto physical = localAreaToGlobal (r) * nativeScaleFactor * getDesktopScaleFactor(); + return { 0, 0, physical.getWidth(), physical.getHeight() }; + } - if (currentPeer != nullptr) - { - currentPeer->addScaleFactorListener (this); - nativeScaleFactor = (float) currentPeer->getPlatformScaleFactor(); - } + /* Convert from the hosted VST3's coordinate system to the component's coordinate system. */ + Rectangle vst3ToComponentRect (const ViewRect& vr) const + { + return getLocalArea (nullptr, Rectangle { vr.right, vr.bottom } / (nativeScaleFactor * getDesktopScaleFactor())); } void componentMovedOrResized (bool, bool wasResized) override @@ -1438,44 +1438,34 @@ private: if (recursiveResize || ! wasResized || getTopLevelComponent()->getPeer() == nullptr) return; - ViewRect rect; - if (view->canResize() == kResultTrue) { - rect.right = (Steinberg::int32) roundToInt ((float) getWidth() * nativeScaleFactor); - rect.bottom = (Steinberg::int32) roundToInt ((float) getHeight() * nativeScaleFactor); - + auto rect = componentToVST3Rect (getLocalBounds()); view->checkSizeConstraint (&rect); { const ScopedValueSetter recursiveResizeSetter (recursiveResize, true); - setSize (roundToInt ((float) rect.getWidth() / nativeScaleFactor), - roundToInt ((float) rect.getHeight() / nativeScaleFactor)); + const auto logicalSize = vst3ToComponentRect (rect); + setSize (logicalSize.getWidth(), logicalSize.getHeight()); } - #if JUCE_WINDOWS - setPluginWindowPos (rect); - #else embeddedComponent.setBounds (getLocalBounds()); - #endif view->onSize (&rect); } else { + ViewRect rect; warnOnFailure (view->getSize (&rect)); - #if JUCE_WINDOWS - setPluginWindowPos (rect); - #else - resizeWithRect (embeddedComponent, rect, nativeScaleFactor); - #endif + resizeWithRect (embeddedComponent, rect); } // Some plugins don't update their cursor correctly when mousing out the window Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); } + using ComponentMovementWatcher::componentMovedOrResized; void componentVisibilityChanged() override @@ -1484,20 +1474,14 @@ private: resizeToFit(); componentMovedOrResized (true, true); } - using ComponentMovementWatcher::componentVisibilityChanged; - void nativeScaleFactorChanged (double newScaleFactor) override - { - nativeScaleFactor = (float) newScaleFactor; - updatePluginScale(); - componentMovedOrResized (false, true); - } + using ComponentMovementWatcher::componentVisibilityChanged; void resizeToFit() { ViewRect rect; warnOnFailure (view->getSize (&rect)); - resizeWithRect (*this, rect, nativeScaleFactor); + resizeWithRect (*this, rect); } tresult PLUGIN_API resizeView (IPlugView* incomingView, ViewRect* newSize) override @@ -1506,34 +1490,28 @@ private: if (incomingView != nullptr && newSize != nullptr && incomingView == view) { - auto scaleToViewRect = [this] (int dimension) - { - return (Steinberg::int32) roundToInt ((float) dimension * nativeScaleFactor); - }; - - auto oldWidth = scaleToViewRect (getWidth()); - auto oldHeight = scaleToViewRect (getHeight()); - - resizeWithRect (embeddedComponent, *newSize, nativeScaleFactor); + const auto oldPhysicalSize = componentToVST3Rect (getLocalBounds()); + const auto logicalSize = vst3ToComponentRect (*newSize); + setSize (logicalSize.getWidth(), logicalSize.getHeight()); + embeddedComponent.setSize (logicalSize.getWidth(), logicalSize.getHeight()); #if JUCE_WINDOWS - setPluginWindowPos (*newSize); + embeddedComponent.updateHWNDBounds(); + #elif JUCE_LINUX || JUCE_BSD + embeddedComponent.updateEmbeddedBounds(); #endif - setSize (embeddedComponent.getWidth(), embeddedComponent.getHeight()); - // According to the VST3 Workflow Diagrams, a resizeView from the plugin should // always trigger a response from the host which confirms the new size. - ViewRect rect { 0, 0, - scaleToViewRect (getWidth()), - scaleToViewRect (getHeight()) }; + auto currentPhysicalSize = componentToVST3Rect (getLocalBounds()); - if (rect.right != oldWidth || rect.bottom != oldHeight + if (currentPhysicalSize.getWidth() != oldPhysicalSize.getWidth() + || currentPhysicalSize.getHeight() != oldPhysicalSize.getHeight() || ! isInOnSize) { // Guard against plug-ins immediately calling resizeView() with the same size const ScopedValueSetter inOnSizeSetter (isInOnSize, true); - view->onSize (&rect); + view->onSize (¤tPhysicalSize); } return kResultTrue; @@ -1544,10 +1522,11 @@ private: } //============================================================================== - static void resizeWithRect (Component& comp, const ViewRect& rect, float scaleFactor) + void resizeWithRect (Component& comp, const ViewRect& rect) const { - comp.setSize (jmax (10, std::abs (roundToInt ((float) rect.getWidth() / scaleFactor))), - jmax (10, std::abs (roundToInt ((float) rect.getHeight() / scaleFactor)))); + const auto logicalSize = vst3ToComponentRect (rect); + comp.setSize (jmax (10, logicalSize.getWidth()), + jmax (10, logicalSize.getHeight())); } void attachPluginWindow() @@ -1555,19 +1534,16 @@ private: if (pluginHandle == HandleFormat{}) { #if JUCE_WINDOWS - if (auto* topComp = getTopLevelComponent()) - { - peer.reset (embeddedComponent.createNewPeer (0, topComp->getWindowHandle())); - pluginHandle = (HandleFormat) peer->getNativeHandle(); - } - #else + pluginHandle = static_cast (embeddedComponent.getHWND()); + #endif + embeddedComponent.setBounds (getLocalBounds()); addAndMakeVisible (embeddedComponent); - #if JUCE_MAC - pluginHandle = (HandleFormat) embeddedComponent.getView(); - #elif JUCE_LINUX || JUCE_BSD - pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID(); - #endif + + #if JUCE_MAC + pluginHandle = (HandleFormat) embeddedComponent.getView(); + #elif JUCE_LINUX || JUCE_BSD + pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID(); #endif if (pluginHandle == HandleFormat{}) @@ -1586,16 +1562,6 @@ private: } } - void removeScaleFactorListener() - { - if (currentPeer == nullptr) - return; - - for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) - if (ComponentPeer::getPeer (i) == currentPeer) - currentPeer->removeScaleFactorListener (this); - } - void updatePluginScale() { if (scaleInterface != nullptr) @@ -1608,7 +1574,7 @@ private: { if (scaleInterface != nullptr) { - const auto result = scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor); + const auto result = scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) getEffectiveScale()); ignoreUnused (result); #if ! JUCE_MAC @@ -1617,38 +1583,49 @@ private: } } + void setScaleFactor (float s) override + { + userScaleFactor = s; + setContentScaleFactor(); + resizeToFit(); + } + + float getEffectiveScale() const + { + return nativeScaleFactor * userScaleFactor; + } + //============================================================================== Atomic refCount { 1 }; VSTComSmartPtr view; #if JUCE_WINDOWS - struct ChildComponent : public Component - { - ChildComponent() { setOpaque (true); } - void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); } - using Component::createNewPeer; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildComponent) - }; + using HandleFormat = HWND; - void setPluginWindowPos (ViewRect rect) + struct ViewComponent : public HWNDComponent { - if (auto* topComp = getTopLevelComponent()) + ViewComponent() { - auto pos = (topComp->getLocalPoint (this, Point()) * nativeScaleFactor).roundToInt(); + setOpaque (true); + inner.addToDesktop (0); - ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHandle }; - - SetWindowPos (pluginHandle, nullptr, - pos.x, pos.y, - rect.getWidth(), rect.getHeight(), - isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); + if (auto* peer = inner.getPeer()) + setHWND (peer->getNativeHandle()); } - } - ChildComponent embeddedComponent; - std::unique_ptr peer; - using HandleFormat = HWND; + void paint (Graphics& g) override { g.fillAll (Colours::black); } + + private: + struct Inner : public Component + { + Inner() { setOpaque (true); } + void paint (Graphics& g) override { g.fillAll (Colours::black); } + }; + + Inner inner; + }; + + ViewComponent embeddedComponent; #elif JUCE_MAC NSViewComponentWithParent embeddedComponent; using HandleFormat = NSView*; @@ -1664,9 +1641,35 @@ private: HandleFormat pluginHandle = {}; bool recursiveResize = false, isInOnSize = false, attachedCalled = false; - ComponentPeer* currentPeer = nullptr; Steinberg::IPlugViewContentScaleSupport* scaleInterface = nullptr; float nativeScaleFactor = 1.0f; + float userScaleFactor = 1.0f; + + struct ScaleNotifierCallback + { + VST3PluginWindow& window; + + void operator() (float platformScale) const + { + MessageManager::callAsync ([ref = Component::SafePointer (&window), platformScale] + { + if (auto* r = ref.getComponent()) + { + r->nativeScaleFactor = platformScale; + r->setContentScaleFactor(); + r->resizeToFit(); + + #if JUCE_WINDOWS + r->embeddedComponent.updateHWNDBounds(); + #elif JUCE_LINUX || JUCE_BSD + r->embeddedComponent.updateEmbeddedBounds(); + #endif + } + }); + } + }; + + NativeScaleFactorNotifier scaleNotifier { this, ScaleNotifierCallback { *this } }; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) @@ -2206,32 +2209,7 @@ public: ~VST3PluginInstance() override { - struct VST3Deleter : public CallbackMessage - { - VST3Deleter (VST3PluginInstance& inInstance, WaitableEvent& inEvent) - : vst3Instance (inInstance), completionSignal (inEvent) - {} - - void messageCallback() override - { - vst3Instance.cleanup(); - completionSignal.signal(); - } - - VST3PluginInstance& vst3Instance; - WaitableEvent& completionSignal; - }; - - if (MessageManager::getInstance()->isThisTheMessageThread()) - { - cleanup(); - } - else - { - WaitableEvent completionEvent; - (new VST3Deleter (*this, completionEvent))->post(); - completionEvent.wait(); - } + callOnMessageThread ([this] { cleanup(); }); } void cleanup() @@ -2988,7 +2966,6 @@ public: ignoreUnused (data, sizeInBytes); } - private: //============================================================================== #if JUCE_LINUX || JUCE_BSD @@ -3318,7 +3295,7 @@ private: /** @note An IPlugView, when first created, should start with a ref-count of 1! */ IPlugView* tryCreatingView() const { - JUCE_ASSERT_MESSAGE_THREAD + JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED IPlugView* v = editController->createView (Vst::ViewType::kEditor); diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index b0a050a33e..86c1eea9c2 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1083,34 +1083,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, ~VSTPluginInstance() override { if (vstEffect != nullptr && vstEffect->magic == 0x56737450 /* 'VstP' */) - { - struct VSTDeleter : public CallbackMessage - { - VSTDeleter (VSTPluginInstance& inInstance, WaitableEvent& inEvent) - : vstInstance (inInstance), completionSignal (inEvent) - {} - - void messageCallback() override - { - vstInstance.cleanup(); - completionSignal.signal(); - } - - VSTPluginInstance& vstInstance; - WaitableEvent& completionSignal; - }; - - if (MessageManager::getInstance()->isThisTheMessageThread()) - { - cleanup(); - } - else - { - WaitableEvent completionEvent; - (new VSTDeleter (*this, completionEvent))->post(); - completionEvent.wait(); - } - } + callOnMessageThread ([this] { cleanup(); }); } void cleanup() @@ -1982,20 +1955,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, return false; } - bool updateSizeFromEditor (int w, int h) - { - editorSize = { w, h }; - - if (auto* editor = getActiveEditor()) - { - editor->setSize (w, h); - return true; - } - - return false; - } - - Rectangle getEditorSize() const { return editorSize; } + bool updateSizeFromEditor (int w, int h); Vst2::AEffect* vstEffect; ModuleHandle::Ptr vstModule; @@ -2076,7 +2036,6 @@ private: AudioBuffer tmpBufferDouble; HeapBlock channelBufferDouble; std::unique_ptr bypassParam; - Rectangle editorSize; std::unique_ptr xmlInfo; @@ -2141,24 +2100,11 @@ private: void setWindowSize (int width, int height) { - if (auto* ed = getActiveEditor()) - { - #if JUCE_LINUX || JUCE_BSD - const MessageManagerLock mmLock; - #endif - - #if ! JUCE_MAC - if (auto* peer = ed->getTopLevelComponent()->getPeer()) - { - auto scale = peer->getPlatformScaleFactor(); - updateSizeFromEditor (roundToInt (width / scale), roundToInt (height / scale)); - - return; - } - #endif + #if JUCE_LINUX || JUCE_BSD + const MessageManagerLock mmLock; + #endif - updateSizeFromEditor (width, height); - } + updateSizeFromEditor (width, height); } //============================================================================== @@ -2769,7 +2715,6 @@ static Array activeVSTWindows; struct VSTPluginWindow : public AudioProcessorEditor, #if ! JUCE_MAC private ComponentMovementWatcher, - private ComponentPeer::ScaleFactorListener, #endif private Timer { @@ -2813,6 +2758,10 @@ public: setOpaque (true); setVisible (true); + + #if JUCE_WINDOWS + addAndMakeVisible (embeddedComponent); + #endif } ~VSTPluginWindow() override @@ -2824,8 +2773,6 @@ public: carbonWrapper.reset(); #endif cocoaWrapper.reset(); - #else - removeScaleFactorListeners(); #endif activeVSTWindows.removeFirstMatchingValue (this); @@ -2833,10 +2780,36 @@ public: } //============================================================================== - void updateSizeFromEditor (int w, int h) + /* Convert from the hosted VST's coordinate system to the component's coordinate system. */ + Rectangle vstToComponentRect (Component& editor, const Rectangle& vr) const { - if (! plugin.updateSizeFromEditor (w, h)) - setSize (w, h); + return editor.getLocalArea (nullptr, vr / (nativeScaleFactor * getDesktopScaleFactor())); + } + + Rectangle componentToVstRect (Component& editor, const Rectangle& vr) const + { + if (auto* tl = editor.getTopLevelComponent()) + return tl->getLocalArea (&editor, vr) * nativeScaleFactor * tl->getDesktopScaleFactor(); + + return {}; + } + + bool updateSizeFromEditor (int w, int h) + { + const auto correctedBounds = vstToComponentRect (*this, { w, h }); + setSize (correctedBounds.getWidth(), correctedBounds.getHeight()); + + #if JUCE_MAC + #if JUCE_SUPPORT_CARBON + if (carbonWrapper != nullptr) + carbonWrapper->setSize (correctedBounds.getWidth(), correctedBounds.getHeight()); + #endif + + if (cocoaWrapper != nullptr) + cocoaWrapper->setSize (correctedBounds.getWidth(), correctedBounds.getHeight()); + #endif + + return true; } #if JUCE_MAC @@ -2870,6 +2843,11 @@ public: void parentHierarchyChanged() override { visibilityChanged(); } #else + float getEffectiveScale() const + { + return nativeScaleFactor * userScaleFactor; + } + void paint (Graphics& g) override { #if JUCE_LINUX || JUCE_BSD @@ -2877,7 +2855,7 @@ public: { if (pluginWindow != 0) { - auto clip = g.getClipBounds(); + auto clip = componentToVstRect (*this, g.getClipBounds().toNearestInt()); X11Symbols::getInstance()->xClearArea (display, pluginWindow, clip.getX(), clip.getY(), static_cast (clip.getWidth()), @@ -2896,36 +2874,15 @@ public: if (recursiveResize) return; - if (auto* peer = getTopLevelComponent()->getPeer()) + if (getPeer() != nullptr) { const ScopedValueSetter recursiveResizeSetter (recursiveResize, true); - const auto pos = (peer->getAreaCoveredBy (*this).toFloat() * nativeScaleFactor).toNearestInt(); - #if JUCE_WINDOWS - if (pluginHWND != 0) - { - ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; - SetWindowPos (pluginHWND, - HWND_BOTTOM, - pos.getX(), - pos.getY(), - 0, - 0, - SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); - - MessageManager::callAsync ([ref = SafePointer (this)] - { - // Clean up after the editor window, in case it tried to move itself - // into the wrong location over this component. - if (ref == nullptr) - return; - - if (auto* p = ref->getPeer()) - p->repaint (p->getBounds().withPosition ({})); - }); - } + embeddedComponent.setBounds (getLocalBounds()); #elif JUCE_LINUX || JUCE_BSD + const auto pos = componentToVstRect (*this, getLocalBounds()); + if (pluginWindow != 0) { auto* symbols = X11Symbols::getInstance(); @@ -2951,8 +2908,7 @@ public: else if (! shouldAvoidDeletingWindow()) closePluginWindow(); - if (auto* peer = getTopLevelComponent()->getPeer()) - setScaleFactorAndDispatchMessage (peer->getPlatformScaleFactor()); + setContentScaleFactor(); #if JUCE_LINUX || JUCE_BSD MessageManager::callAsync ([safeThis = SafePointer { this }] @@ -2971,38 +2927,32 @@ public: { closePluginWindow(); openPluginWindow(); - - removeScaleFactorListeners(); - - if (auto* peer = getTopLevelComponent()->getPeer()) - peer->addScaleFactorListener (this); - componentMovedOrResized (true, true); } - void nativeScaleFactorChanged (double newScaleFactor) override + void setContentScaleFactor() { - setScaleFactorAndDispatchMessage (newScaleFactor); - #if JUCE_WINDOWS - resizeToFit(); - #endif - } - - void setScaleFactorAndDispatchMessage (double newScaleFactor) - { - if (approximatelyEqual ((float) newScaleFactor, nativeScaleFactor)) - return; - - nativeScaleFactor = (float) newScaleFactor; - if (pluginRespondsToDPIChanges) dispatch (Vst2::effVendorSpecific, (int) ByteOrder::bigEndianInt ("PreS"), (int) ByteOrder::bigEndianInt ("AeCs"), - nullptr, nativeScaleFactor); + nullptr, getEffectiveScale()); } #endif + void setScaleFactor (float scale) override + { + userScaleFactor = scale; + + #if ! JUCE_MAC + setContentScaleFactor(); + #endif + + #if JUCE_WINDOWS + resizeToFit(); + #endif + } + //============================================================================== bool keyStateChanged (bool) override { return pluginWantsKeys; } bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; } @@ -3025,7 +2975,14 @@ public: if (! reentrantGuard) { reentrantGuard = true; + + #if JUCE_WINDOWS + // Some plugins may draw/resize inside their idle callback, so ensure that + // DPI awareness is set correctly inside this call. + ScopedThreadDPIAwarenessSetter scope (getPluginHWND()); + #endif plugin.dispatch (Vst2::effEditIdle, 0, 0, nullptr, 0); + reentrantGuard = false; } @@ -3124,7 +3081,7 @@ private: #else void openPluginWindow() { - if (isOpen || getWindowHandle() == nullptr) + if (isOpen) return; JUCE_VST_LOG ("Opening VST UI: " + plugin.getName()); @@ -3132,13 +3089,18 @@ private: pluginRespondsToDPIChanges = plugin.pluginCanDo ("supportsViewDpiScaling") > 0; - if (auto* peer = getTopLevelComponent()->getPeer()) - setScaleFactorAndDispatchMessage (peer->getPlatformScaleFactor()); + setContentScaleFactor(); Vst2::ERect* rect = nullptr; dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0); - dispatch (Vst2::effEditOpen, 0, 0, getWindowHandle(), 0); + + #if JUCE_WINDOWS + auto* handle = embeddedComponent.getHWND(); + #else + auto* handle = getWindowHandle(); + #endif + dispatch (Vst2::effEditOpen, 0, 0, handle, 0); dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0); // do this before and after like in the steinberg example dispatch (Vst2::effGetProgram, 0, 0, nullptr, 0); // also in steinberg code @@ -3146,7 +3108,7 @@ private: #if JUCE_WINDOWS originalWndProc = 0; - pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); + auto* pluginHWND = getPluginHWND(); if (pluginHWND == 0) { @@ -3189,7 +3151,7 @@ private: ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; SetWindowPos (pluginHWND, 0, - 0, 0, roundToInt (rw * nativeScaleFactor), roundToInt (rh * nativeScaleFactor), + 0, 0, rw, rh, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); GetWindowRect (pluginHWND, &r); @@ -3225,9 +3187,6 @@ private: X11Symbols::getInstance()->xMapRaised (display, pluginWindow); #endif - w = roundToInt ((float) w / nativeScaleFactor); - h = roundToInt ((float) h / nativeScaleFactor); - // double-check it's not too tiny w = jmax (w, 32); h = jmax (h, 32); @@ -3241,13 +3200,6 @@ private: startTimer (18 + juce::Random::getSystemRandom().nextInt (5)); repaint(); } - - void removeScaleFactorListeners() - { - for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) - if (auto* peer = ComponentPeer::getPeer (i)) - peer->removeScaleFactorListener (this); - } #endif //============================================================================== @@ -3266,12 +3218,13 @@ private: #if JUCE_WINDOWS JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4244) + auto* pluginHWND = getPluginHWND(); + if (originalWndProc != 0 && pluginHWND != 0 && IsWindow (pluginHWND)) SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc); JUCE_END_IGNORE_WARNINGS_MSVC originalWndProc = 0; - pluginHWND = 0; #elif JUCE_LINUX || JUCE_BSD pluginWindow = 0; #endif @@ -3291,7 +3244,8 @@ private: if (pluginRefusesToResize) return true; - return (isWithin (w, getWidth(), 5) && isWithin (h, getHeight(), 5)); + const auto converted = vstToComponentRect (*this, { w, h }); + return (isWithin (converted.getWidth(), getWidth(), 5) && isWithin (converted.getHeight(), getHeight(), 5)); } void resizeToFit() @@ -3299,14 +3253,16 @@ private: Vst2::ERect* rect = nullptr; dispatch (Vst2::effEditGetRect, 0, 0, &rect, 0); - auto w = roundToInt ((rect->right - rect->left) / nativeScaleFactor); - auto h = roundToInt ((rect->bottom - rect->top) / nativeScaleFactor); + auto w = rect->right - rect->left; + auto h = rect->bottom - rect->top; if (! isWindowSizeCorrectForPlugin (w, h)) { updateSizeFromEditor (w, h); sizeCheckCount = 0; } + + embeddedComponent.updateHWNDBounds(); } void checkPluginWindowSize() @@ -3322,7 +3278,9 @@ private: { Component::SafePointer w (activeVSTWindows[i]); - if (w != nullptr && w->pluginHWND == hW) + auto* pluginHWND = w->getPluginHWND(); + + if (w != nullptr && pluginHWND == hW) { if (message == WM_CHAR || message == WM_KEYDOWN @@ -3337,7 +3295,7 @@ private: if (w != nullptr) // (may have been deleted in SendMessage callback) return CallWindowProc ((WNDPROC) w->originalWndProc, - (HWND) w->pluginHWND, + (HWND) pluginHWND, message, wParam, lParam); } } @@ -3439,32 +3397,82 @@ private: void resized() override { - #if JUCE_SUPPORT_CARBON - if (carbonWrapper != nullptr) - carbonWrapper->setSize (getWidth(), getHeight()); - #endif - - if (cocoaWrapper != nullptr) - cocoaWrapper->setSize (getWidth(), getHeight()); } #endif //============================================================================== VSTPluginInstance& plugin; + float userScaleFactor = 1.0f; bool isOpen = false, recursiveResize = false; bool pluginWantsKeys = false, pluginRefusesToResize = false, alreadyInside = false; #if ! JUCE_MAC bool pluginRespondsToDPIChanges = false; + float nativeScaleFactor = 1.0f; + + struct ScaleNotifierCallback + { + VSTPluginWindow& window; + + void operator() (float platformScale) const + { + MessageManager::callAsync ([ref = Component::SafePointer (&window), platformScale] + { + if (auto* r = ref.getComponent()) + { + r->nativeScaleFactor = platformScale; + r->setContentScaleFactor(); + + #if JUCE_WINDOWS + r->resizeToFit(); + #endif + r->componentMovedOrResized (true, true); + } + }); + } + }; + + NativeScaleFactorNotifier scaleNotifier { this, ScaleNotifierCallback { *this } }; + #if JUCE_WINDOWS - HWND pluginHWND = {}; + struct ViewComponent : public HWNDComponent + { + ViewComponent() + { + setOpaque (true); + inner.addToDesktop (0); + + if (auto* peer = inner.getPeer()) + setHWND (peer->getNativeHandle()); + } + + void paint (Graphics& g) override { g.fillAll (Colours::black); } + + private: + struct Inner : public Component + { + Inner() { setOpaque (true); } + void paint (Graphics& g) override { g.fillAll (Colours::black); } + }; + + Inner inner; + }; + + HWND getPluginHWND() const + { + return GetWindow ((HWND) embeddedComponent.getHWND(), GW_CHILD); + } + + ViewComponent embeddedComponent; void* originalWndProc = {}; int sizeCheckCount = 0; #elif JUCE_LINUX || JUCE_BSD ::Display* display = XWindowSystem::getInstance()->getDisplay(); Window pluginWindow = 0; #endif + #else + static constexpr auto nativeScaleFactor = 1.0f; #endif //============================================================================== @@ -3485,6 +3493,14 @@ AudioProcessorEditor* VSTPluginInstance::createEditor() #endif } +bool VSTPluginInstance::updateSizeFromEditor (int w, int h) +{ + if (auto* editor = dynamic_cast (getActiveEditor())) + return editor->updateSizeFromEditor (w, h); + + return false; +} + //============================================================================== // entry point for all callbacks from the plugin static pointer_sized_int VSTCALLBACK audioMaster (Vst2::AEffect* effect, int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt) diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp index f018f5f672..0ce3bb272c 100644 --- a/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/modules/juce_audio_processors/juce_audio_processors.cpp @@ -77,6 +77,26 @@ static bool arrayContainsPlugin (const OwnedArray& list, #endif +template +void callOnMessageThread (Callback&& callback) +{ + if (MessageManager::getInstance()->existsAndIsLockedByCurrentThread()) + { + callback(); + return; + } + + WaitableEvent completionEvent; + + MessageManager::callAsync ([&callback, &completionEvent] + { + callback(); + completionEvent.signal(); + }); + + completionEvent.wait(); +} + #if JUCE_MAC //============================================================================== @@ -203,6 +223,7 @@ private: #include "utilities/juce_ParameterAttachments.cpp" #include "utilities/juce_AudioProcessorValueTreeState.cpp" #include "utilities/juce_PluginHostType.cpp" +#include "utilities/juce_NativeScaleFactorNotifier.cpp" #if JUCE_UNIT_TESTS #include "format_types/juce_VST3PluginFormat_test.cpp" diff --git a/modules/juce_audio_processors/juce_audio_processors.h b/modules/juce_audio_processors/juce_audio_processors.h index 490a188bbc..1b2acbbfe8 100644 --- a/modules/juce_audio_processors/juce_audio_processors.h +++ b/modules/juce_audio_processors/juce_audio_processors.h @@ -111,6 +111,7 @@ //============================================================================== #include "utilities/juce_VSTCallbackHandler.h" #include "utilities/juce_VST3ClientExtensions.h" +#include "utilities/juce_NativeScaleFactorNotifier.h" #include "utilities/juce_ExtensionsVisitor.h" #include "processors/juce_AudioProcessorParameter.h" #include "processors/juce_HostedAudioProcessorParameter.h" diff --git a/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.cpp b/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.cpp new file mode 100644 index 0000000000..0528f9d826 --- /dev/null +++ b/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.cpp @@ -0,0 +1,59 @@ +/* + ============================================================================== + + This file is part of the JUCE 7 technical preview. + Copyright (c) 2022 - Raw Material Software Limited + + You may use this code under the terms of the GPL v3 + (see www.gnu.org/licenses). + + For the technical preview this file cannot be licensed commercially. + + 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 +{ + +static void removeScaleFactorListenerFromAllPeers (ComponentPeer::ScaleFactorListener& listener) +{ + for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) + ComponentPeer::getPeer (i)->removeScaleFactorListener (&listener); +} + +NativeScaleFactorNotifier::NativeScaleFactorNotifier (Component* comp, std::function onScaleChanged) + : ComponentMovementWatcher (comp), + scaleChanged (std::move (onScaleChanged)) +{ + componentPeerChanged(); +} + +NativeScaleFactorNotifier::~NativeScaleFactorNotifier() +{ + removeScaleFactorListenerFromAllPeers (*this); +} + +void NativeScaleFactorNotifier::nativeScaleFactorChanged (double newScaleFactor) +{ + NullCheckedInvocation::invoke (scaleChanged, (float) newScaleFactor); +} + +void NativeScaleFactorNotifier::componentPeerChanged() +{ + removeScaleFactorListenerFromAllPeers (*this); + + if (auto* x = getComponent()) + peer = x->getPeer(); + + if (auto* x = peer) + { + x->addScaleFactorListener (this); + nativeScaleFactorChanged (x->getPlatformScaleFactor()); + } +} + +} // namespace juce diff --git a/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h b/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h new file mode 100644 index 0000000000..ac8e92a8ee --- /dev/null +++ b/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h @@ -0,0 +1,60 @@ +/* + ============================================================================== + + This file is part of the JUCE 7 technical preview. + Copyright (c) 2022 - Raw Material Software Limited + + You may use this code under the terms of the GPL v3 + (see www.gnu.org/licenses). + + For the technical preview this file cannot be licensed commercially. + + 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 +{ + +/** + Calls a function every time the native scale factor of a component's peer changes. + + This is used in the VST and VST3 wrappers to ensure that the editor's scale is kept in sync with + the scale of its containing component. +*/ +class NativeScaleFactorNotifier : private ComponentMovementWatcher, + private ComponentPeer::ScaleFactorListener +{ +public: + /** Constructs an instance. + + While the instance is alive, it will listen for changes to the scale factor of the + comp's peer, and will call onScaleChanged whenever this scale factor changes. + + @param comp The component to observe + @param onScaleChanged A function that will be called when the backing scale factor changes + */ + NativeScaleFactorNotifier (Component* comp, std::function onScaleChanged); + ~NativeScaleFactorNotifier() override; + +private: + void nativeScaleFactorChanged (double newScaleFactor) override; + void componentPeerChanged() override; + + using ComponentMovementWatcher::componentVisibilityChanged; + void componentVisibilityChanged() override {} + + using ComponentMovementWatcher::componentMovedOrResized; + void componentMovedOrResized (bool, bool) override {} + + ComponentPeer* peer = nullptr; + std::function scaleChanged; + + JUCE_DECLARE_NON_COPYABLE (NativeScaleFactorNotifier) + JUCE_DECLARE_NON_MOVEABLE (NativeScaleFactorNotifier) +}; + +} // namespace juce diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index e42d29d055..7fd1fff004 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -465,17 +465,18 @@ static bool isPerMonitorDPIAwareWindow (HWND nativeWindow) #endif } -static bool isPerMonitorDPIAwareThread() +static bool isPerMonitorDPIAwareThread (GetThreadDPIAwarenessContextFunc getThreadDPIAwarenessContextIn = getThreadDPIAwarenessContext, + GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromDPIAwarenessContextIn = getAwarenessFromDPIAwarenessContext) { #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE return false; #else setDPIAwareness(); - if (getThreadDPIAwarenessContext != nullptr - && getAwarenessFromDPIAwarenessContext != nullptr) + if (getThreadDPIAwarenessContextIn != nullptr + && getAwarenessFromDPIAwarenessContextIn != nullptr) { - return (getAwarenessFromDPIAwarenessContext (getThreadDPIAwarenessContext()) + return (getAwarenessFromDPIAwarenessContextIn (getThreadDPIAwarenessContextIn()) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware); } @@ -569,12 +570,17 @@ ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() = default; ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { - if (! isPerMonitorDPIAwareThread()) + static auto localGetThreadDpiAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext"); + static auto localGetAwarenessFromDpiAwarenessContextFunc = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext"); + + if (! isPerMonitorDPIAwareThread (localGetThreadDpiAwarenessContext, localGetAwarenessFromDpiAwarenessContextFunc)) return; - if (setThreadDPIAwarenessContext != nullptr) + static auto localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); + + if (localSetThreadDPIAwarenessContext != nullptr) { - previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); + previousContext = localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); #if JUCE_DEBUG ++numActiveScopedDpiAwarenessDisablers; @@ -586,7 +592,10 @@ ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() { if (previousContext != nullptr) { - setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); + static auto localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); + + if (localSetThreadDPIAwarenessContext != nullptr) + localSetThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); #if JUCE_DEBUG --numActiveScopedDpiAwarenessDisablers; diff --git a/modules/juce_gui_extra/embedding/juce_HWNDComponent.h b/modules/juce_gui_extra/embedding/juce_HWNDComponent.h index aa73e770b4..019b877deb 100644 --- a/modules/juce_gui_extra/embedding/juce_HWNDComponent.h +++ b/modules/juce_gui_extra/embedding/juce_HWNDComponent.h @@ -64,6 +64,9 @@ public: /** Resizes this component to fit the HWND that it contains. */ void resizeToFit(); + /** Forces the bounds of the HWND to match the bounds of this component. */ + void updateHWNDBounds(); + /** @internal */ void paint (Graphics&) override; diff --git a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp index bf535a2b56..9680c4af0d 100644 --- a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp +++ b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp @@ -610,7 +610,7 @@ private: if (auto* peer = owner.getPeer()) { auto r = peer->getComponent().getLocalArea (&owner, owner.getLocalBounds()); - return r * peer->getPlatformScaleFactor(); + return r * peer->getPlatformScaleFactor() * peer->getComponent().getDesktopScaleFactor(); } return owner.getLocalBounds(); diff --git a/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp b/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp index 37320316e1..594d43d866 100644 --- a/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp +++ b/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp @@ -165,4 +165,10 @@ void HWNDComponent::resizeToFit() setBounds (pimpl->getHWNDBounds()); } +void HWNDComponent::updateHWNDBounds() +{ + if (pimpl != nullptr) + pimpl->componentMovedOrResized (true, true); +} + } // namespace juce