ToggleButton not always trigger

JUCE 8.0.10 with Linux Ubuntu 20.0.4, happens with Reaper and Bitwig. I was able to reproduce it with the VST2 version of our plugin in Reaper. No problem on all other OS’s.

A user reported that the ToggleButtons do not trigger when the mouse is not moving. He needs the mouse a bit afterward to make the buttons trigger.
I was able to reproduce it with the touchpad in a Linux VM. It didn’t happen every time with the touchpad. Probably every 10-20th time.

It only happened with the VST2 plugin and our CLAP. It also looks our other plugins don’t have this issue, even though they use the same button and the same JUCE. I think this is timing related (edit: also other plugins with many UI components are affected).
The buttons are assigned to their parameter with an attachment and they also do onStateChanged() to set flags. We don’t do any special things here.

Any anyone noticed similar problems on Linux? Any Ideas what the problem could be?

The following video illustrates the problem. The first click in the video does not trigger the button. Moving the mouse afterward triggers the button. The other clicks worked. The problem happens randomly.

Nothing special in the code. Here the construction:

    _eqBypassButton = std::make_unique<TalTextButton>("BYP");
    _eqBypassButton->onClick =  [this]
    {
        _graphicEq->setAlpha(_eqBypassButton->getToggleState() ? 0.5f : 1.0f);
    };
    _eqBypassButtonAt = std::make_unique<AudioProcessorValueTreeState::ButtonAttachment>(*p._params->treeState, p._params->paramBypass, *_eqBypassButton);
    addAndMakeVisible(_eqBypassButton.get());
    // set bounds in the resize method

The ToggleButton class only overwrites the paint method and forwards the mouseUp and mouseDown button events to the base class (kickButton is not enabled here):

class TalTextButton : public juce::ToggleButton
{
    juce::String _text;
    bool _isKickButton;

protected:
    const bool _abButton;

public:
    TalTextButton (juce::String text, bool isKickButton = false, bool abButton = false)
            : ToggleButton(text), _isKickButton(isKickButton), _abButton(abButton)
    {
        _text = text;
        setWantsKeyboardFocus (false);
        _isKickButton = isKickButton;
    }

    ~TalTextButton() override = default;

    void mouseUp (const juce::MouseEvent& event) override
    {
        if (_isKickButton)
        {
            this->setToggleState(false, juce::sendNotification);
        }
        else
        {
            ToggleButton::mouseUp(event);
        }

        repaint();
    }

    void mouseDown (const juce::MouseEvent& event) override
    {
        if (_isKickButton)
        {
            setToggleState(true, juce::sendNotification);
        }
        else
        {
            ToggleButton::mouseDown(event);
        }

        repaint();
    }

    void paintButton (juce::Graphics& g, bool, bool) override
    {
        // ...
    }
};

Perhaps you can attach the to debuger to see whether & when the plugin actually receives the mouse down? To me the button does not receive the first mouse down at all. But it does not explain why it only happens on VST2.

    void mouseDown (const juce::MouseEvent& event) override
    {
        std::cout << "mouse down" << std::endl;
        ...
    }

Thanks for the idea. Did some tests with the log.

What happens when the click fails:

  • Most of the time mouseUp and mouseDown are not triggered when click. In rarer cases, it also happens that only mouseUp is not triggered.
  • They trigger afterwards when the mouse moves. No mouse event gets lost.

To exclude different things I tried the following, but the problem persists:

  • Without ButtonAttachment
  • Without onClick action
  • Without timerCallback actions on the main component

I can also reproduce it with other plugins with JUCE 8.0.10. But not with all. I wasn’t able to reproduce this on simpler plugins with only a few knobs and buttons.

I wonder how mouse events can get stuck in this way. Any ideas?

Edit:
The problem can’t be in the ToggleButton itself. It has to be in the JUCE code that calls the Component mouse down / up functions. It looks like it stops processing the mouse event queue too early in some cases and does not process all events.

I found following comment in juce_XWindowSystem_linux.cpp. Only VST2 and CLAP (via JUCE Wrapper) are affected. So I think something probably goes wrong there:

For plugins, the host (generally) provides some kind of run loop mechanism instead.
- In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can
  dispatch all pending events inside this callback. The host doesn't know about any of the
  plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready,
  and its associated callback being called.

Maybe some unlucky timing between the host and the plug-in. This also explains the sometimes sluggish UI behaviour with the JUCE VST2 on Linux.

1 Like

I couldn’t repro this on Ubuntu 24. I’ll give Ubuntu 20 a go next.

No problem on all other OS’s.

Does that mean you can confirm that other Ubuntu versions work well?

This problem is now fixed for JUCE CLAP wrapper plugins. The wrapper now uses the same approach VST3 uses, but it looks like VST2 does not support this.

The only thing i know is that the user has Wayland/Linux. I was able to reproduce on a standard Ubuntu 20. I’m pretty sure this is a general Linux / VST2 problem.

But it is probably hard to reproduce with a simple demo plugin. It happens randomly and not with all our plugins. Because of this I think it is timing related and maybe it depends on how much load the main thread has (paint, timerCallbacks and other things).

The user reported two issues:

  • Stuck click events in the queue that trigger only after he moves the mouse
  • General slow and somehow sluggish mouse interaction (button clicks, mouse drag…) Probably this is by design (comment in JUCE code)

Probably under some circumstances (some race condition), we may miss or ignore an effEditIdle call and do not always empty the event queue. But this is just a wild guess.