I’m raising my concern about a small detail regarding how JUCE handles keyboard focus/input when you’re using JUCE to build you own VST/AU plugin hosting applications, such as a DAW. This tiny detail basically renders JUCE unusable in DAW types of applications:
Clicking the hosted VST/AU plugin’s GUI grabs the keyboard focus completely from the main JUCE application, which sound like a logical thing to happen at first glance. However, plugins are meant to be used in DAW applications, which most often use the spacebar key to start/stop the song playback and other key combinations to quckly save your project, etc. Now if there is no way for the main JUCE application to receive any keyboard events while the user is tweaking the plugin GUI elements (knobs/sliders/etc.), the JUCE application cannot start/stop playback, or save the project, before the user clicks the main JUCE application area itself, outside the plugin’s GUI.
This almost defeats the whole point of having VST/AU plugin hosting support in JUCE, since DAWs are the main thing which host such plugins. And if you can’t perform standard DAW functions while having plugins on screen, the users get frustrated and/or confused why the JUCE application isn’t reacting as expected.
Is there an easy way to hack my way around this limitation?
Have you tested that this is actually a bug in JUCE (and not some bug in your code)?
For example, I’ve just opened Waveform 11 (a DAW made with JUCE) and I was able to be focused on the plugin while clicking spacebar to trigger play/stop/etc.
From the plugin makers point of view, you get a shot at the key with KeyPressed(). If you return false from that event that will pass that event to the host. For example we only process spacebar if we’re on standalone mode, but otherwise we give it back to the host.
Here’s a small test project, which hosts plugins and writes on screen changing key press counter whenever a key has been pressed. Compile and run it to see how it works on your computer. Just right click with mouse to select/load a VST/AU plugin. Then click the plugin’s GUI area and start pressing computer keyboard keys. Let me know if it works on your computer. On mine I get no keyboard presses until I click outside the plugin GUI area.
I think that this is by design. If the plugin is focused, it gets to respond to key events first, and only unhandled key events should be passed up to the host.
Perhaps there’s scope for a new addGlobalKeyboardListener feature on juce::Desktop to mirror the existing addGlobalMouseListener - however, this would not be trivial to add and would require a lot of testing. I don’t see this happening in the near future as we’re currently focusing on other major features.
From what I see in your testing code, the problem is you’ve added the editor directly as a child to MainComponent with addAndMakeVisible(*mp_editor);
Usually the way it’s done in most hosts (JUCE and non-JUCE) is to add the plugin into its own window. I think doing that will greatly simplify what you’re doing and solve problems like that.
Using the ready made window fixes the issue only if I wouldn’t need to add my own Components into the same window next to the plugin GUI. (Namely audio routing and modulation/automation related parameters) For this I need to have a separate parent Component inside the window which contains the plugin GUI and my extra Components next to it. This in turn raises the current issue demonstrated in this thread.
You can still add other Components to the external window, this shouldn’t cause a conflict with spacebar and other keyboard shortcuts that should probably be processed by the main host window and not the plugin window.
You can just compare the behavior against other DAWs (JUCE and non-JUCE).
For example Waveform 11 also adds custom Components to the plugin window (see image) but the spacebar gets correctly passed to the host window even in focus.
You can also derive your plugin Window from KeyListener if you need to handle keystrokes yourself. I do this for my plugin which hosts 3rd party plugins but needs to handle the spacebar in Standalone.
Also check the JucePlugin_EditorRequiresKeyboardFocus setting.
What is that “JucePlugin_EditorRequiresKeyboardFocus” setting? Where can it be found? I couldn’t find any reference to it from the documentation nor by Googling.
Doesn’t seem to work on Windows. On Mac using AU/VST3 plugins things work OK, but on Windows the keyboard events don’t get received by the KeyListener, after you’ve tweaked the plugin’s GUI knobs/etc…
Here’s my short PluginWindow class derived from DocumentWindow and KeyListener:
class PluginWindow : public juce::DocumentWindow,
public juce::KeyListener
{
public:
PluginWindow(const juce::String& name,
juce::Component* p_plugin_editor
: juce::DocumentWindow(name, juce::Colour(0xFF000000), DocumentWindow::TitleBarButtons::closeButton)
{
setUsingNativeTitleBar(true);
setVisible(true);
setAlwaysOnTop(true);
p_plugin_editor->addKeyListener(this);
setContentComponentSize(p_plugin_editor->getWidth(), p_plugin_editor->getHeight());
setContentNonOwned(p_plugin_editor, true);
}
void closeButtonPressed() override
{
clearContentComponent();
delete this;
}
bool keyPressed(const KeyPress& key) override
{
// Windows VST3 doesn't end up here when you tweak knobs and press computer's keys!
return false;
}
bool keyStateChanged(bool isKeyDown) override
{
// Windows VST3 doesn't end up here when you tweak knobs and press computer's keys!
return false;
}
bool keyPressed(const KeyPress& key, Component* originatingComponent) override
{
// Windows VST3 doesn't end up here when you tweak knobs and press computer's keys!
return false;
}
bool keyStateChanged(bool isKeyDown, Component* originatingComponent) override
{
// Windows VST3 doesn't end up here when you tweak knobs and press computer's keys!
return false;
}
private:
juce::TooltipWindow m_tool_tip_window { this, 500 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginWindow)
};
The p_plugin_editor I give to the constructor is a juce::Component, which has some of my own Components and also the VST3 plugin GUI as child Components.
That means the native plugin window has consumed the event. I’m guessing that’s what you will also experience in other DAWs, and is probably the ‘correct’ behavior.
The plugin needs to report it doesn’t consume the event for it to be passed to the host.
That cannot be the case, that it would happen in other DAWs also. The plugins that are misbehaving are all of Native Instruments / U-he plugins. In any DAW it’s possible to successfully press spacebar once you’ve tweaked any of NI / U-he plugin GUI elements.
Is there a way to steal the keyboard event information before it is given to the plugin?
AudioPluginHost is also buggy on Windows. So it has the very same issue I’m describing here. I.e. ComponentPeer::handleKeyPress doesn’t get called at all when you have been tweaking VST plugin’s knobs right before you press some key.
Can you confirm that AudioPluginHost, on your Windows computer, calls ComponentPeer::handleKeyPress when you have tweaked GUI elements (knobs/sliders) of a VST3 plugin with mouse and then press some key on computer’s keyboard?
Yes, I’ve just compiled APH from source on Windows and it works correctly with the VST3 plugin I’ve tested, which is a commercial plugin of ours that consumes some keys but not spacebar.
But just to make sure - it only works correctly if the plugin didn’t consume the event. If the plugin consumes the event, I don’t get handleKeyPress in the host, which I suspect is what will happen in most hosts to avoid a key command doing 2 things at the same time.
You should really compare/debug other DAWs and plugins to see what’s the expected behavior is.
From the tests I’ve already done as described above, here are my observations on Windows:
Ableton Live reacts happily to any keyboard presses (Spacebar for example) after VST3 plugin’s knobs have been touched with mouse.
AudioPluginHost, nor my own application, does not react at all to any keyboard presses after VST3 plugin’s knobs have been touched with mouse.
I can only check the ComponentPeer::handleKeyPress issue on AudioPluginHost and my own app, since they are the only ones I can debug. And as I already mentioned, that method won’t get called.
I’m not sure if there is much else that can be debugged this way?