godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
87.37k stars 19.63k forks source link

Linux: Tablet pen pressure randomly jumps to 1 #53033

Open willgfx opened 2 years ago

willgfx commented 2 years ago

Godot version

3.3.3.stable

System information

Linux (EndeavourOS [Arch]), Huion Kamvas Pro 20 Tablet Display & XPS 13 7390 2-in-1 Screen

Issue description

This seems to be a Linux-only issue, I am not able to reproduce the bug on Windows with the same hardware.

Tablet pressure value will jump to 1 randomly for periods of 1 to 2 frames. These do not correspond to dumps from usbhid-dump. (https://digimend.github.io/support/howto/trbl/locating_failure/), and I am not seeing the issue in other software (Krita, MyPaint, GNOME Settings tablet tester).

Tested on both a Huion Kamvas Pro 20 Tablet Display and XPS 13 7390 2-in-1 tablet.

Steps to reproduce

func _input(event):
    if event is InputEventMouseMotion:
        print (event.get_pressure())

Use the tablet pen at varying levels of pressure. You will see the printed pressure value randomly jump to 1 for 1 to 2 frame periods. Screenshot from 2021-09-24 18-54-20

Minimal reproduction project

TabletPressure.zip

Calinou commented 2 years ago

cc @bruvzg

willgfx commented 2 years ago

For now, this GDScript workaround can be used to correct the issue in your projects until there's an engine-side fix:

var pressure
var pressure_buf = [0, 0] # past pressure value buffer

func _input(event):
    if event is InputEventMouseMotion:
        pressure = event.get_pressure()

        # If a pressure value of 1 is encountered, "correct" the value by
        # extrapolating from the delta of the past two values. This will 
        # correct the jumping to 1 error while also allowing values that
        # are "supposed" to be 1.
        if pressure == 1 && pressure_buf[0] != 0:
            pressure = min(1, pressure_buf[0] + pressure_buf[0] - pressure_buf[1])

        pressure_buf.pop_back()
        pressure_buf.push_front(pressure)
madmiraal commented 2 years ago

This happens when the mouse is moved and it sends it's own Mouse Motion event. There are three problems here:

  1. As identified in #23439 and related to https://github.com/godotengine/godot-proposals/issues/426, events don't provide the device id; so there is no way to tell that these Mouse Motion events come from a different device.
  2. Mouse Button events from one device are affecting all devices. #38439 sets a non-pressure device e.g. a mouse's Mouse Motion pressure to 1.0 if the left button is pressed. However, the mouse's button is not pressed. Instead, the pen, which will correctly set the left button to pressed when the pen touches the pad, is being used by the mouse's Mouse Motion event to indicate that the pressure should be set to 1.0.
  3. The mouse is not being disabled when the tablet is being used. Any attempt to move the mouse (a relative device) will be overridden by the tablet (an absolute) device. So the easiest solution would be to disable the mouse's Mouse Motion events when the tablet is being used. However, there will still be a need to internally store the mouse's Mouse Button events separately (problem 2); so they can be applied when the tablet stops being used.