o3de icon indicating copy to clipboard operation
o3de copied to clipboard

Linux: mouse_delta_x listens to the mouse wheel input

Open LB-ArturZieba opened this issue 2 years ago • 5 comments

Describe the bug On Linux, mouse_delta_x Input listens to both the mouse X axis movement and the mouse wheel scrolling. Mouse wheel scrolling should be picked up only by the mouse_delta_z Input.

This issue does not occur on Windows.

Assets required Script Canvas and Input Bindings shown on the image below are required to reproduce this issue: mouse_delta_xSCandIB

Steps to reproduce

  1. Create an entity, add Script Canvas and Input components to it.
  2. Assign assets mentioned in the Assets required section to their respective components.
  3. Enter the Game Mode (for example Ctrl + G).
  4. Scroll the mouse wheel.

Expected behavior Nothing happens.

Actual behavior Script Canvas prints output to the Console.

Video

https://user-images.githubusercontent.com/86952082/222140406-25d5c95d-b617-4e75-9dad-6cc914c854f7.mp4

Found in Branch Development (a775010)

Commit ID from o3de/o3de Repository a775010c5c3ea6b067df2ef0a398fbe02bb54434

Desktop

  • Device: PC
  • OS: Linux
  • Version: Ubuntu 22.04.1 LTS
  • CPU AMD Ryzen 5 3600
  • GPU NVIDIA GeForce RTX 2060 SUPER
  • Memory 16GB

LB-ArturZieba avatar Mar 01 '23 12:03 LB-ArturZieba

I can verify this also happens on my end, this has been the case in 2210, 2305, and 2310. And this issue is not isolated to just script canvas, it even occurs when using the Fly Camera component with the Starting Point Input gem disabled.

gralco avatar Jul 25 '23 20:07 gralco

This happens on my end, but debugging into it, it seems like when I roll the mouse wheel, I get a xcb_input_raw_motion_event_t with an axis value that is legitimate:

case XCB_INPUT_RAW_MOTION:
            {
                const xcb_input_raw_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_raw_motion_event_t*>(event);

                int axisLen = xcb_input_raw_button_press_axisvalues_length(mouseMotionEvent);
                const xcb_input_fp3232_t* axisvalues = xcb_input_raw_button_press_axisvalues_raw(mouseMotionEvent);
                for (int i = 0; i < axisLen; ++i)
                {
                    const float axisValue = fp3232ToFloat(axisvalues[i]);

                    switch (i)
                    {
                    case 0:
                        QueueRawMovementEvent(InputDeviceMouse::Movement::X, axisValue);
                        break;
                    case 1:
                        QueueRawMovementEvent(InputDeviceMouse::Movement::Y, axisValue);
                        break;
                    }
                }
            }
            ```
It enters the Movement::X block...  Note that this same thing happens when you move the mouse around horizontally.  I'm not sure why I'm getting a horizontal axis event from XCB when I only roll the mouse wheel.  Perhaps there is something else in the event that informs that its a wheel event

nick-l-o3de avatar Jan 23 '24 20:01 nick-l-o3de

There's nothing in the event struct (I inspected it in the debugger) coming from XCB to discern between a mouse axis (x and y movement) and wheel event. The device id and other fields are identical. Maybe there's some way to discern in the XCB api?

What I'm seeing in the debugger is when you roll the mouse wheel and nothing else, we get an XCB generic event that is of type button press for the wheel axis, and then button release for the wheel axis, and then we get a raw motion event for button 0 as well (that is, event->detail is 0). The event contains 2 axes. Only the 0th axis moves.

The problem is that the same exact event happens if you move the mouse (raw motion event, with 2 axes, but these axes move).

The only thing that seems different between actual movement events and wheel events is that the event always seems to have 2 axes available in move events, and 1 axis available in wheel events. Could use this to discern between the two, but surely theres a better way to ask it what virtual axis it is, or button?

nick-l-o3de avatar Jan 23 '24 20:01 nick-l-o3de

I think I figured it out. I'll upload a fix soon. It looks like in XCB (poorly documented) a xcb_input_raw_motion_event_t is also a raw button press, and inside that struct is a valuator bitmask. (A valuator is an axis of a device that isn't binary, such as a button press).

so this code should fix it

uint32_t mask = *(xcb_input_raw_button_press_valuator_mask(mouseMotionEvent));
                if ((mask & 0x3) == 0)
                {
                    // x and y movement are not present in the mask.  This is not necessarily a wheel roll,
                    // but its also not x and y movement.  What it is depends on how many axes the device actually has.
                    // Since we capture wheel roll as button presses (above), ignore this.
                    break;
                }
                ```

The button press valuator mask will contain 0x1 (horizontal axis), 0x2 (vertical axis), or more typically in all the tests I did, (0x1 | 0x2) == 0x3, meaning, 2 axes have been updated and they are x and y axes.   For all other motion events the valuator mask will contain different bits (for example if you have a mouse with a Z axis it may have 0x4).  The mouse wheel appears to always report in as valuator 0x8.

nick-l-o3de avatar Jan 24 '24 20:01 nick-l-o3de

Note that my solution was chosen since it impacts the smallest amount of code I could find.

Another solution would be to stop using the button press wheel and use the axis instead. Or add a new axis to the mouse input API in O3DE that represents 'smooth' scrolling from the axis rather than 'wheel clicks'.

nick-l-o3de avatar Jan 25 '24 18:01 nick-l-o3de