Browse Source

Plugin Hosting: Take custom scaling into account when computing editor sizes

pull/22/head
reuk 3 years ago
parent
commit
c30b3a1a31
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
11 changed files with 434 additions and 282 deletions
  1. +1
    -1
      modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  2. +104
    -127
      modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  3. +161
    -145
      modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  4. +21
    -0
      modules/juce_audio_processors/juce_audio_processors.cpp
  5. +1
    -0
      modules/juce_audio_processors/juce_audio_processors.h
  6. +59
    -0
      modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.cpp
  7. +60
    -0
      modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h
  8. +17
    -8
      modules/juce_gui_basics/native/juce_win32_Windowing.cpp
  9. +3
    -0
      modules/juce_gui_extra/embedding/juce_HWNDComponent.h
  10. +1
    -1
      modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp
  11. +6
    -0
      modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp

+ 1
- 1
modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -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
//==============================================================================


+ 104
- 127
modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -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<int> 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<int> vst3ToComponentRect (const ViewRect& vr) const
{
return getLocalArea (nullptr, Rectangle<int> { 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<bool> 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<bool> inOnSizeSetter (isInOnSize, true);
view->onSize (&rect);
view->onSize (&currentPhysicalSize);
}
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<HWND> (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<int> refCount { 1 };
VSTComSmartPtr<IPlugView> 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<int>()) * 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<ComponentPeer> 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<VST3PluginWindow> (&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);


+ 161
- 145
modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -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<int> getEditorSize() const { return editorSize; }
bool updateSizeFromEditor (int w, int h);
Vst2::AEffect* vstEffect;
ModuleHandle::Ptr vstModule;
@@ -2076,7 +2036,6 @@ private:
AudioBuffer<double> tmpBufferDouble;
HeapBlock<double*> channelBufferDouble;
std::unique_ptr<VST2BypassParameter> bypassParam;
Rectangle<int> editorSize;
std::unique_ptr<VSTXMLInfo> 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<VSTPluginWindow*> 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<int> vstToComponentRect (Component& editor, const Rectangle<int>& vr) const
{
if (! plugin.updateSizeFromEditor (w, h))
setSize (w, h);
return editor.getLocalArea (nullptr, vr / (nativeScaleFactor * getDesktopScaleFactor()));
}
Rectangle<int> componentToVstRect (Component& editor, const Rectangle<int>& 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<unsigned int> (clip.getWidth()),
@@ -2896,36 +2874,15 @@ public:
if (recursiveResize)
return;
if (auto* peer = getTopLevelComponent()->getPeer())
if (getPeer() != nullptr)
{
const ScopedValueSetter<bool> 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<Component> (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<VSTPluginWindow> { 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<VSTPluginWindow> 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<VSTPluginWindow> (&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<VSTPluginWindow*> (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)


+ 21
- 0
modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -77,6 +77,26 @@ static bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
#endif
template <typename Callback>
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"


+ 1
- 0
modules/juce_audio_processors/juce_audio_processors.h View File

@@ -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"


+ 59
- 0
modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.cpp View File

@@ -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<void (float)> 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

+ 60
- 0
modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h View File

@@ -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<void (float)> 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<void (float)> scaleChanged;
JUCE_DECLARE_NON_COPYABLE (NativeScaleFactorNotifier)
JUCE_DECLARE_NON_MOVEABLE (NativeScaleFactorNotifier)
};
} // namespace juce

+ 17
- 8
modules/juce_gui_basics/native/juce_win32_Windowing.cpp View File

@@ -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;


+ 3
- 0
modules/juce_gui_extra/embedding/juce_HWNDComponent.h View File

@@ -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;


+ 1
- 1
modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp View File

@@ -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();


+ 6
- 0
modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp View File

@@ -165,4 +165,10 @@ void HWNDComponent::resizeToFit()
setBounds (pimpl->getHWNDBounds());
}
void HWNDComponent::updateHWNDBounds()
{
if (pimpl != nullptr)
pimpl->componentMovedOrResized (true, true);
}
} // namespace juce

Loading…
Cancel
Save