1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-14 00:14:18 +00:00

VST3: Added HWNDComponentWithParent class for Windows hosting and removed platform-specific IPlugViewContentScaleSupport implementation

This commit is contained in:
ed 2021-02-19 17:31:01 +00:00
parent cb57904740
commit 455e08da3f
2 changed files with 148 additions and 127 deletions

View file

@ -31,10 +31,6 @@
namespace juce namespace juce
{ {
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
extern void setThreadDPIAwarenessForWindow (HWND);
#endif
using namespace Steinberg; using namespace Steinberg;
//============================================================================== //==============================================================================
@ -1140,9 +1136,7 @@ private:
//============================================================================== //==============================================================================
struct VST3PluginWindow : public AudioProcessorEditor, struct VST3PluginWindow : public AudioProcessorEditor,
public ComponentMovementWatcher, public ComponentMovementWatcher,
#if JUCE_WINDOWS || JUCE_LINUX
public ComponentPeer::ScaleFactorListener, public ComponentPeer::ScaleFactorListener,
#endif
public IPlugFrame public IPlugFrame
{ {
VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView) VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView)
@ -1155,26 +1149,20 @@ struct VST3PluginWindow : public AudioProcessorEditor,
setVisible (true); setVisible (true);
warnOnFailure (view->setFrame (this)); warnOnFailure (view->setFrame (this));
#if ! JUCE_MAC
view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (void**) &scaleInterface); view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (void**) &scaleInterface);
#endif
resizeToFit(); resizeToFit();
} }
~VST3PluginWindow() override ~VST3PluginWindow() override
{ {
#if ! JUCE_MAC
if (scaleInterface != nullptr) if (scaleInterface != nullptr)
scaleInterface->release(); scaleInterface->release();
removeScaleFactorListeners(); removeScaleFactorListener();
#if JUCE_LINUX #if JUCE_LINUX
embeddedComponent.removeClient(); embeddedComponent.removeClient();
#endif #endif
#endif
warnOnFailure (view->removed()); warnOnFailure (view->removed());
warnOnFailure (view->setFrame (nullptr)); warnOnFailure (view->setFrame (nullptr));
@ -1183,6 +1171,8 @@ struct VST3PluginWindow : public AudioProcessorEditor,
#if JUCE_MAC #if JUCE_MAC
embeddedComponent.setView (nullptr); embeddedComponent.setView (nullptr);
#elif JUCE_WINDOWS
embeddedComponent.setHWND (nullptr);
#endif #endif
view = nullptr; view = nullptr;
@ -1323,79 +1313,50 @@ struct VST3PluginWindow : public AudioProcessorEditor,
//============================================================================== //==============================================================================
void componentPeerChanged() override void componentPeerChanged() override
{ {
#if ! JUCE_MAC removeScaleFactorListener();
removeScaleFactorListeners(); currentPeer = getTopLevelComponent()->getPeer();
if (auto* topPeer = getTopLevelComponent()->getPeer()) if (currentPeer != nullptr)
topPeer->addScaleFactorListener (this); {
#endif currentPeer->addScaleFactorListener (this);
nativeScaleFactor = (float) currentPeer->getPlatformScaleFactor();
}
} }
void componentMovedOrResized (bool, bool wasResized) override void componentMovedOrResized (bool, bool wasResized) override
{ {
if (recursiveResize) if (recursiveResize || ! wasResized || getTopLevelComponent()->getPeer() == nullptr)
return; return;
auto* topComp = getTopLevelComponent(); ViewRect rect;
if (topComp->getPeer() != nullptr) if (view->canResize() == kResultTrue)
{ {
#if JUCE_WINDOWS rect.right = (Steinberg::int32) roundToInt ((float) getWidth() * nativeScaleFactor);
auto pos = (topComp->getLocalPoint (this, Point<int>()) * nativeScaleFactor).roundToInt(); rect.bottom = (Steinberg::int32) roundToInt ((float) getHeight() * nativeScaleFactor);
#endif
const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true); view->checkSizeConstraint (&rect);
ViewRect rect;
if (wasResized && view->canResize() == kResultTrue)
{ {
rect.right = (Steinberg::int32) roundToInt ((float) getWidth() * nativeScaleFactor); const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
rect.bottom = (Steinberg::int32) roundToInt ((float) getHeight() * nativeScaleFactor);
view->checkSizeConstraint (&rect); setSize (roundToInt ((float) rect.getWidth() / nativeScaleFactor),
roundToInt ((float) rect.getHeight() / nativeScaleFactor));
auto w = roundToInt ((float) rect.getWidth() / nativeScaleFactor);
auto h = roundToInt ((float) rect.getHeight() / nativeScaleFactor);
setSize (w, h);
#if JUCE_WINDOWS
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHandle);
#endif
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#else
embeddedComponent.setBounds (getLocalBounds());
#endif
view->onSize (&rect);
}
else
{
warnOnFailure (view->getSize (&rect));
#if JUCE_WINDOWS
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHandle);
#endif
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#else
embeddedComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif
} }
// Some plugins don't update their cursor correctly when mousing out the window embeddedComponent.setBounds (getLocalBounds());
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
view->onSize (&rect);
}
else
{
warnOnFailure (view->getSize (&rect));
resizeWithRect (embeddedComponent, rect, nativeScaleFactor);
} }
}
// Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
}
using ComponentMovementWatcher::componentMovedOrResized; using ComponentMovementWatcher::componentMovedOrResized;
void componentVisibilityChanged() override void componentVisibilityChanged() override
@ -1407,13 +1368,11 @@ struct VST3PluginWindow : public AudioProcessorEditor,
componentMovedOrResized (true, true); componentMovedOrResized (true, true);
} }
using ComponentMovementWatcher::componentVisibilityChanged; using ComponentMovementWatcher::componentVisibilityChanged;
#if JUCE_WINDOWS || JUCE_LINUX
void nativeScaleFactorChanged (double newScaleFactor) override void nativeScaleFactorChanged (double newScaleFactor) override
{ {
if (pluginHandle == 0 || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor)) if (pluginHandle == HandleFormat{} || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor))
return; return;
nativeScaleFactor = (float) newScaleFactor; nativeScaleFactor = (float) newScaleFactor;
@ -1421,7 +1380,6 @@ struct VST3PluginWindow : public AudioProcessorEditor,
if (scaleInterface != nullptr) if (scaleInterface != nullptr)
scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor); scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor);
} }
#endif
void resizeToFit() void resizeToFit()
{ {
@ -1458,73 +1416,48 @@ private:
void attachPluginWindow() void attachPluginWindow()
{ {
#if JUCE_MAC if (pluginHandle == HandleFormat{})
if (pluginHandle == nil)
#else
if (pluginHandle == 0)
#endif
{ {
#if JUCE_WINDOWS
if (auto* topComp = getTopLevelComponent())
{
peer.reset (embeddedComponent.createNewPeer (0, topComp->getWindowHandle()));
pluginHandle = (HandleFormat) peer->getNativeHandle();
nativeScaleFactor = (float) peer->getPlatformScaleFactor();
}
#else
embeddedComponent.setBounds (getLocalBounds()); embeddedComponent.setBounds (getLocalBounds());
addAndMakeVisible (embeddedComponent); addAndMakeVisible (embeddedComponent);
#if JUCE_MAC
pluginHandle = (HandleFormat) embeddedComponent.getView();
jassert (pluginHandle != nil);
#elif JUCE_LINUX
pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
jassert (pluginHandle != 0);
#endif
#endif
#if JUCE_MAC #if JUCE_MAC
if (pluginHandle != nil) pluginHandle = (HandleFormat) embeddedComponent.getView();
#else #elif JUCE_WINDOWS
if (pluginHandle != 0) pluginHandle = (HandleFormat) embeddedComponent.getHWND();
#elif JUCE_LINUX
pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
#endif #endif
warnOnFailure (view->attached ((void*) pluginHandle, defaultVST3WindowType));
}
#if ! JUCE_MAC if (pluginHandle == HandleFormat{})
if (auto* topPeer = getTopLevelComponent()->getPeer()) {
{ jassertfalse;
nativeScaleFactor = 1.0f; // force update return;
nativeScaleFactorChanged ((float) topPeer->getPlatformScaleFactor()); }
warnOnFailure (view->attached ((void*) pluginHandle, defaultVST3WindowType));
if (scaleInterface != nullptr)
scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor);
} }
#endif
} }
#if ! JUCE_MAC void removeScaleFactorListener()
void removeScaleFactorListeners()
{ {
if (currentPeer == nullptr)
return;
for (int i = 0; i < ComponentPeer::getNumPeers(); ++i) for (int i = 0; i < ComponentPeer::getNumPeers(); ++i)
if (auto* p = ComponentPeer::getPeer (i)) if (ComponentPeer::getPeer (i) == currentPeer)
p->removeScaleFactorListener (this); currentPeer->removeScaleFactorListener (this);
} }
#endif
//============================================================================== //==============================================================================
Atomic<int> refCount { 1 }; Atomic<int> refCount { 1 };
VSTComSmartPtr<IPlugView> view; VSTComSmartPtr<IPlugView> view;
#if JUCE_WINDOWS #if JUCE_WINDOWS
struct ChildComponent : public Component HWNDComponentWithParent embeddedComponent;
{
ChildComponent() {}
void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); }
using Component::createNewPeer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildComponent)
};
ChildComponent embeddedComponent;
std::unique_ptr<ComponentPeer> peer;
using HandleFormat = HWND; using HandleFormat = HWND;
#elif JUCE_MAC #elif JUCE_MAC
AutoResizingNSViewComponentWithParent embeddedComponent; AutoResizingNSViewComponentWithParent embeddedComponent;
@ -1538,14 +1471,11 @@ private:
#endif #endif
HandleFormat pluginHandle = {}; HandleFormat pluginHandle = {};
bool recursiveResize = false; bool recursiveResize = false, hasDoneInitialResize = false;
#if ! JUCE_MAC ComponentPeer* currentPeer = nullptr;
Steinberg::IPlugViewContentScaleSupport* scaleInterface = nullptr; Steinberg::IPlugViewContentScaleSupport* scaleInterface = nullptr;
#endif
float nativeScaleFactor = 1.0f; float nativeScaleFactor = 1.0f;
bool hasDoneInitialResize = false;
//============================================================================== //==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow)

View file

@ -81,7 +81,97 @@ static bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
#endif #endif
#if JUCE_MAC || JUCE_IOS #if JUCE_WINDOWS
//==============================================================================
class HWNDComponentWithParent : public HWNDComponent,
private Timer
{
public:
HWNDComponentWithParent()
{
String className ("JUCE_");
className << String::toHexString (Time::getHighResolutionTicks());
HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle();
WNDCLASSEX wc = {};
wc.cbSize = sizeof (wc);
wc.lpfnWndProc = (WNDPROC) wndProc;
wc.cbWndExtra = 4;
wc.hInstance = moduleHandle;
wc.lpszClassName = className.toWideCharPointer();
atom = RegisterClassEx (&wc);
jassert (atom != 0);
hwnd = CreateWindow (getClassNameFromAtom(), L"HWNDComponentWithParent",
0, 0, 0, 0, 0,
nullptr, nullptr, moduleHandle, nullptr);
jassert (hwnd != nullptr);
setHWND (hwnd);
startTimer (30);
}
~HWNDComponentWithParent() override
{
if (IsWindow (hwnd))
DestroyWindow (hwnd);
UnregisterClass (getClassNameFromAtom(), nullptr);
}
private:
//==============================================================================
static LRESULT CALLBACK wndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam)
{
if (message == WM_SHOWWINDOW && wParam == TRUE)
return 0;
return DefWindowProc (h, message, wParam, lParam);
}
void timerCallback() override
{
if (HWND child = getChildHWND())
{
stopTimer();
ShowWindow (child, SW_HIDE);
SetParent (child, NULL);
auto windowFlags = GetWindowLongPtr (child, -16);
windowFlags &= ~WS_CHILD;
windowFlags |= WS_POPUP;
SetWindowLongPtr (child, -16, windowFlags);
setHWND (child);
}
}
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
HWND getChildHWND() const
{
if (HWND parent = (HWND) getHWND())
return GetWindow (parent, GW_CHILD);
return nullptr;
}
//==============================================================================
ATOM atom;
HWND hwnd;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentWithParent)
};
#elif JUCE_MAC || JUCE_IOS
#if JUCE_IOS #if JUCE_IOS
#define JUCE_IOS_MAC_VIEW UIView #define JUCE_IOS_MAC_VIEW UIView
@ -130,6 +220,7 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
} }
} }
}; };
#endif #endif
} // namespace juce } // namespace juce