VST plugin isn't getting keystrokes


#1

Hi all,

In my plugin I have a Label control (the same happens with a TextEditor).
When I want to start editing (single or double mouse click), I can see the control changed to allow the editing, but it completely ignores all keystrokes.

When I do that with Ableton Live and I try pushing up/down arrows on my keyboard I can see the host application reacting; even with the plugin GUI window at top level and focused.

And the same happens widh Cubase SX 3.

Am I missing something? How can I let the keyboard work with a plugin?
…I’m already calling setWantsKeyboardFocus(true), by the way…

Thanks,
toot


#2

there is currently no answer to this. live eats all keyboard events for itself from the juce vst plugin. i believe cubase may recieve some, i may be mistaken, but i know that it still responds to its own shortcut keys.

tracktion and energyXT are safe from this (so you can load your plugs in eXT inside live and it works) but that’s obviously a pain.

until we can figure out what exactly live is playing at, there’s no solution.


#3

before than now the plugin was working with another framework, wxWidgets, and it worked perfectly with text editors, so it shouldn’t be impossible to do.

I’m a bit puzzled now: I have to allow users to type a name for a preset file, how could I do that then?


#4

Did you set the JucePlugin_EditorRequiresKeyboardFocus flag in the plugin characteristics header file?


#5

Of course (I didn’t touch it from the demo plugin as it either is suffering the same problem when you try to type a new value for the gain with the keyboard).

I did some investigations, hope it could help you to figure out what’s happening:

  1. there are plugins where text editing just works, with Cubase but even with Live
  2. with Spy++ I can see that my juce VST plugin is receiving the keystrokes from the system but it seems to ignore them
  3. setting some breakpoints in juce_win32_Windowing.cpp I don’t see the peerWindowProc called for WM_KEYDOWN, WM_KEYUP, WM_CHAR messages, I don’t know why; but seems logical that since our code is ignoring them then they just get bounced back to the higher level parent windows (the host) so it “looks” like the host is catching keystrokes…
  4. I also have seen (I didn’t know that before) that Juce doesn’t create additional windows to implement the controls, it just manages the big container… maybe other plugins are creating “regular” edit controls that implicitly are able to get keystrokes, being those standard system controls
  5. also I have seen that until ver. 2.3 the VSTSDK was managing keystrokes with some comunication with the host and that those functions have been deprecated in ver. 2.4. I’m trying to recompile my plugin with ver. 2.3. I’m suspecting that it could have a chance to work better…

#6

Yeah, the hosts do catch keystrokes - I had to do that in tracktion, but also pass them on to the plugin in case it needs them. If the window events never arrive, then the only way is to create a separate window. I’ve seen other plugins that use that trick. You could do it quite neatly in juce by popping up a small borderless modal window containing just the editor.


#7

I have the same problem with Live 6 and a juce-based plugin. Based on Toot’s observation I guessed that Live is somehow hijacking the windowProc of the juce container window, injecting its own handler before juce’s. That alien handler would then eat all the WM_KEYXXX messages, leaving us with nothing left but mouse stuff.

And it indeed that seems to be the case. Try changing windowProc() in juce_win32_Windowing.cpp to the following and you get your keystrokes back:

[i]
static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
{
{
[color=blue] // begin mod
LONG_PTR lp = GetWindowLongPtr(h, GWLP_WNDPROC);
if (lp != (LONG_PTR)windowProc)
{
juce::Logger::outputDebugString(T(“alien windowproc detected”));
SetWindowLongPtr(h, GWLP_WNDPROC, (LONG_PTR)windowProc);
juce::Logger::outputDebugString(T(“restored regular windowproc”));
}
// end mod
[/color] Win32ComponentPeer* const peer = getOwnerOfWindow (h);
const MessageManagerLock messLock;
if (ComponentPeer::isValidPeer (peer))
return peer->peerWindowProc (h, message, wParam, lParam);
}

    return DefWindowProc (h, message, wParam, lParam);
}

[/i]

It seems to work, but don’t know if this is the polite and robust thing to do though. There might be legimitate reasons too why a host would want to see our messages. On the other hand, Live grabbing all keyboard stuff without asking wasn’t too polite either to begin with… :slight_smile:

(If anybody of Ableton is reading this: love your product, really do, but please offer a preference setting in Live to let keystrokes pass to the plugin. Cubase4 overrides the windowproc too, but at least they pass the keystrokes)

What do you guys think? Would it make sense to build this alien windowproc detection into the juce vst wrapper?


#8

I’d be a bit cautious about doing this kind of thing to override the interception, because there could be other reasons why it’s adding the hook… Maybe it’s possible to unhook their callback, but still call it when we get the callback? (A sort of double-hook…)


#9

Jules, I agree that it’s not without risks.

The double hook you mentioned is indeed a more cautious idea, but if I understand correctly there might be a loop problem:

original situation:
-Live windowproc eats WM_KEYDOWN/UP, then calls juce windowproc with the leftover messages (i.e. only mouse stuff)

my prev suggestion:
-juce windowproc gets everything, Live windowproc gets nothing

double hook style (if I understand correctly):
-juce_windowproc gets messages first, eats all the WM_KEYUP/DOWN/WM_MOUSE… etc it wants, and sends remainder to Live windowproc
-Live windowproc eats those messages from the remainder it is interested in (but not any more the keystrokes that we now got at first)
-Live windowproc (thinking it’s still first in the chain) calls juce windowproc again with leftover messages : infinite loop problem for messages that neither Live or juce handle??
-so we need a way to detect reentrancy here?

PS i also checked Live’s behaviour on OSX and there it behaves differently : keystrokes are passed correctly to a juce plugin. Maybe it’s an unintended thing on windows after all?


#10

Don’t forget as well that you might want live to get keystrokes sometimes. We really need some way of indicating when a keystroke is unwanted, so it can get passed-on. Bit tricky, that.


#11

makes sense

on a related note: I also have a problem in Sonar6.
It sends keystroke messages allright, but not all of them. For instance cursor up/down didn’t work.

spy++ revealed that Sonar sends a WM_GETDLGCODE message which allows windows controls to express which keystrokes they handle and which ones they leave to windows (e.g. tab or cursor keys in a dialog can be handled by windows itself).

I was able to fix it by adding this to the juce windowproc:

  switch (message)
    {
        case WM_GETDLGCODE: 
            return DLGC_WANTALLKEYS;
       ...

this makes the juce window say to windows : keep sending all keys please.


#12

Ah, that’s interesting, I’d never heard of that message before. Thanks!


#13

Neither did I before I spied it :slight_smile: it’s one of the more obscure ones I guess…

Jules, sorry for monopolising your time, but since we’re on the subject, one last little question popped up when I was toying around with juce_win32_Windowing.cpp. windowProc() doesn’t seem to handle the WM_KEYDOWN event. I mean, it does something with it, but then passes it along to DefWindowProc() I think. I was expecting it rather should return 0, like it does for WM_KEYUP? Maybe I’m missing something here…

    case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
            doKeyDown (wParam);
            break;

        case WM_KEYUP:
        case WM_SYSKEYUP:
            doKeyUp (wParam);
            return 0;

#14

There may be a reason for that, but it might be lost in the depths of time… I can’t think of a good reason why it’d consume one event and not the other.

I’ve a feeling that I might have to tweak the key handling code to make it explicit about whether the key gets used or not, in which case I guess I’ll be altering this code to return 0 if it is used.


#15

yeah, maybe that would also be useful for your double-hook scenario : you could let a VST author express the intention to consume certain keys but let others pass to the host.

Anyway, I experimented a bit with consuming the WM_KEYDOWN in a double hook-style solution and all seems to work fine now with Live. And any other host I tried the same code with.


#16

Hello mucoder,

I am currently trying to fix this Live keystroke problem and your double hook solution seems very interesting.

Could you give us more details on how you’ve done that ? I must admit I am not an expert of win32 event handling …

Regards,
Nicolas


#17

hi Nicolas,
alas the solution I proposed does not work completely. I got the keyboard input working in Live, but later I discovered that when the plugin is under stress, the timing of the midi I/O was affected negatively.
Somehow, Live really wants to be the first in the chain to receive all window events. If not, timing problems occur.

I’m looking for another solution myself but did not have time yet. I guess the cleanest solution would be to do all Juce UI in a separate window and not within the window that Live gives to the plugin. Then Juce could be the sole message listener for that window.

IF you really want the old code I can dig it up. Just let me know…

PS actually the really cleanest solution would come from Ableton itself. Live on Mac for instance does not have this problem, so it’s not by design I guess. I filed a request with this at their support mail address, but never even got an answer.


#18

hi Nicolas,
alas the solution I proposed does not work completely. I got the keyboard input working in Live, but later I discovered that when the plugin is under stress, the timing of the midi I/O was affected negatively.
Somehow, Live really wants to be the first in the chain to receive all window events. If not, timing problems occur.

I’m looking for another solution myself but did not have time yet. I guess the cleanest solution would be to do all Juce UI in a separate window and not within the window that Live gives to the plugin. Then Juce could be the sole message listener for that window.

IF you really want the old code I can dig it up. Just let me know…

PS actually the really cleanest solution would come from Ableton itself. Live on Mac for instance does not have this problem, so it’s not by design I guess. I filed a request with this at their support mail address, but never even got an answer.


#19

Hi mucoder,

Thanks for the feedback. If ever you can find this code back, I would be interested in seeing how you did it.
But you’re right, a fix from Ableton would be much better. I’ll try to contact them as well.

Best,
Nicolas


#20

We have noticed the same problem is in AULab aswell.