Using an absolute pointer, the x or y coordinate of an event is incorrect at the right or bottom edge.
Using an absolute pointer(such as a tablet), the x or y coordinates of an event(such as MotionNotify
) is incorrect at the right or bottom edge. For example, when the screen size is 800x600 and the pointer is at the lower right corner of the screen, (x_root
, y_root
) of a XMotionEvent
is (798, 598) incorrectly, while (799, 599) is expected.
I am testing on Debian 11 virtual machine created by the virt-manager.(But the bug will probably be reproduced on any X11 desktop system.)
I tested using the evtest and xev utility, while moving the pointer to lower right corner of the screen.
This is the result of testing the linux input device, which shows the correct (799, 599) coordinates at the end.
$ sudo evtest
Available devices:
...
/dev/input/event5: spice vdagent tablet
Select the device event number [0-5]: 5
...
Supported events:
...
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 445
Min 0
Max 799
Event code 1 (ABS_Y)
Value 440
Min 0
Max 599
...
Event: time 1664263076.603705, -------------- SYN_REPORT ------------
Event: time 1664263076.620458, type 3 (EV_ABS), code 0 (ABS_X), value 799
Event: time 1664263076.620458, type 3 (EV_ABS), code 1 (ABS_Y), value 599
This is result of testing the X11 input device, which shows the incorrect (798, 598) coordinates at the end.
$ xinput list
...
⎜ ↳ spice vdagent tablet id=10 [slave pointer (2)]
...
$ xinput list-props 10
...
Device 'spice vdagent tablet':
Device Enabled (124): 1
Coordinate Transformation Matrix (126): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
...
libinput Calibration Matrix (267): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
libinput Calibration Matrix Default (268): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
...
Device Node (249): "/dev/input/event5"
...
$ xev
...
MotionNotify event, serial 37, synthetic NO, window 0x2400001,
root 0x53b, subw 0x0, time 23267908, (142,123), root:(798,598),
state 0x100, is_hint 0, same_screen YES
...
I analyzed the source code(master) and found the cause of the bug. This is the relevant code in the 'src/xf86libinput.c'.
#define TOUCH_AXIS_MAX 0xffff
...
xf86libinput_init_pointer_absolute(InputInfoPtr pInfo)
{
...
min = 0;
max = TOUCH_AXIS_MAX;
...
xf86InitValuatorAxisStruct(dev, 0,
XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
min, max, res * 1000, 0, res * 1000, Absolute);
...
}
...
xf86libinput_handle_absmotion(InputInfoPtr pInfo, struct libinput_event_pointer *event)
{
...
x = libinput_event_pointer_get_absolute_x_transformed(event, TOUCH_AXIS_MAX);
...
}
...
At the xf86libinput_init_pointer_absolute()
, the maximum value of the x coordinate is initialized to be 65535. But at the xf86libinput_handle_absmotion()
, the screen width argument of libinput_event_pointer_get_absolute_x_transformed()
is the same 65535, while 65535 + 1 is the correct value.
So, the coordinate conversion results in a wrong number. Like this,
x = (799.0 - 0) * 65535 / (799 + 1) = 65453.08125 # at the xf86libinput_handle_event()
*screenx = x * (800.0 / (65535.0 + 1)) = 798.9878082275391 # at the scale_to_desktop() of the xorg-server
This is the same for the y coordinate.
Patching xf86libinput_handle_absmotion()
like this corrects the problem.
x = libinput_event_pointer_get_absolute_x_transformed(event, TOUCH_AXIS_MAX + 1);
y = libinput_event_pointer_get_absolute_y_transformed(event, TOUCH_AXIS_MAX + 1);
As a side note, I found this bug when I enabled the auto-hide option of the gnome-panel using the gnome-flashback.(It's a critical bug, because the automatically hidden panel does never appear again.)