No Horizontal Scrolling In X11

When testing my plugin in Linux (both Ubuntu and Mint), I noticed the horizontal scrolling didn’t work at all. Looking into JUCE’s source code, it becomes obvious why:

void XWindowSystem::handleWheelEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent, float amount) const
{
    MouseWheelDetails wheel;
    wheel.deltaX = 0.0f;
    wheel.deltaY = amount;
    wheel.isReversed = false;
    wheel.isSmooth = false;
    wheel.isInertial = false;

    peer->handleMouseWheel (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonPressEvent, peer->getPlatformScaleFactor()),
                            getEventTime (buttonPressEvent), wheel);
}

After digging into the source code a bit more, I was able to come up with the following solution, so I figured I’d share it so anyone else can implement it into their JUCE Linux program :slight_smile:

In juce_XWindowSystem_linux.h :

  • Change line 308 in the source to
    void handleWheelEvent (LinuxComponentPeer*, const XButtonPressedEvent&, float, float) const;
  • Change line 346 in the source to
    int pointerMap[7] = {};

In juce_XWindowSystem_linux.cpp :

  • Change the enum MouseButtons (found at line 398 in the source) to
enum MouseButtons
    {
        NoButton = 0,
        LeftButton = 1,
        MiddleButton = 2,
        RightButton = 3,
        WheelUp = 4,
        WheelDown = 5,
        WheelLeft = 6,
        WheelRight = 7
    };
  • Change the function initialisePointerMap (found at line 3063 in the source) to
void XWindowSystem::initialisePointerMap()
{
    auto numButtons = X11Symbols::getInstance()->xGetPointerMapping (display, nullptr, 0);
    pointerMap[2] = pointerMap[3] = pointerMap[4] = pointerMap[5] = pointerMap[6] = Keys::NoButton;

    if (numButtons == 2)
    {
        pointerMap[0] = Keys::LeftButton;
        pointerMap[1] = Keys::RightButton;
    }
    else if (numButtons >= 3)
    {
        pointerMap[0] = Keys::LeftButton;
        pointerMap[1] = Keys::MiddleButton;
        pointerMap[2] = Keys::RightButton;

        if (numButtons >= 5)
        {
            pointerMap[3] = Keys::WheelUp;
            pointerMap[4] = Keys::WheelDown;

            if (numButtons >= 7)
            {
                pointerMap[5] = Keys::WheelLeft;
                pointerMap[6] = Keys::WheelRight;
            }
        }
    }
}
  • Change the function handleWheelEvent (found at line 3571 in the source) to
void XWindowSystem::handleWheelEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent, float amount, float h_amount) const
{
    MouseWheelDetails wheel;
    wheel.deltaX = h_amount;
    wheel.deltaY = amount;
    wheel.isReversed = false;
    wheel.isSmooth = false;
    wheel.isInertial = false;
    
    peer->handleMouseWheel (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonPressEvent, peer->getPlatformScaleFactor()),
                            getEventTime (buttonPressEvent), wheel);
}
  • And finally, change the function handleButtonPressEvent (found at line 3593 in the source) to
void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent) const
{
    updateKeyModifiers ((int) buttonPressEvent.state);

    auto mapIndex = (uint32) (buttonPressEvent.button - Button1);

    if (mapIndex < (uint32) numElementsInArray (pointerMap))
    {
        switch (pointerMap[mapIndex])
        {
            case Keys::WheelLeft:       handleWheelEvent (peer, buttonPressEvent, 0.0f,  100.0f / 256.0f); break;
            case Keys::WheelRight:      handleWheelEvent (peer, buttonPressEvent, 0.0f, -100.0f / 256.0f); break;
            case Keys::WheelUp:         handleWheelEvent (peer, buttonPressEvent,  50.0f / 256.0f, 0.0f); break;
            case Keys::WheelDown:       handleWheelEvent (peer, buttonPressEvent, -50.0f / 256.0f, 0.0f); break;
            case Keys::LeftButton:      handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::leftButtonModifier); break;
            case Keys::RightButton:     handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::rightButtonModifier); break;
            case Keys::MiddleButton:    handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::middleButtonModifier); break;
            default: break;
        }
    }
}

And that should be it! :slight_smile: I only have the one laptop to test this on, so it may need some more fine-tuning. using 50.0f/256.0f in the horizontal scrolling felt too slow and not fluid enough for me so I doubled it, but these values could also be changed depending on what feels more natural

Hope this helps!