diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox index 3050cd80..2ebfa321 100644 --- a/doc/device-configuration-via-udev.dox +++ b/doc/device-configuration-via-udev.dox @@ -162,23 +162,4 @@ model quirks hwdb for instructions. This property must not be used for any other purpose, no specific behavior is guaranteed. -@subsection model_specific_configuration_huion_tablets Graphics tablets without BTN_TOOL_PEN proximity events - -On graphics tablets, the BTN_TOOL_PEN bit signals that the pen is in -detectable range and will send events. When the pen leaves the sensor range, -the bit must be unset to signal that the tablet is out of proximity again. -Some HUION PenTablet devices are buggy and do not send this event. To a -caller, it thus looks like the pen is constantly in proximity. This causes -unexpected behavior in applications that rely on tablet device proximity. - -The property LIBINPUT_MODEL_TABLET_NO_PROXIMITY_OUT may be set -by a user in a local hwdb file. This property designates the tablet -to be buggy and that libinput should work around this bug. - -Many of the affected tablets cannot be detected automatically by libinput -because HUION tablets reuse USB IDs. Local configuration is required to set -this property. Refer to the libinput model quirks hwdb for instructions. - -This property must not be used for any other purpose, no specific behavior -is guaranteed. */ diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 7e9281d1..21501c2b 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1667,12 +1667,14 @@ tablet_proximity_out_quirk_timer_func(uint64_t now, void *data) return; } + tablet->quirks.proximity_out_in_progress = true; ARRAY_FOR_EACH(events, e) { tablet->base.interface->process(&tablet->base, tablet->device, e, now); } + tablet->quirks.proximity_out_in_progress = false; tablet->quirks.proximity_out_forced = true; } @@ -1721,10 +1723,19 @@ tablet_proximity_out_quirk_update(struct tablet_dispatch *tablet, } tablet->quirks.last_event_time = time; } else if (e->type == EV_KEY && e->code == BTN_TOOL_PEN) { - if (e->value) + if (e->value) { tablet_proximity_out_quirk_set_timer(tablet, time); - else + } else { + /* If we get a BTN_TOOL_PEN 0 when *not* injecting + * events it means the tablet will give us the right + * events after all and we can disable our + * timer-based proximity out. + */ + if (!tablet->quirks.proximity_out_in_progress) + tablet->quirks.need_to_force_prox_out = false; + libinput_timer_cancel(&tablet->quirks.prox_out_timer); + } } } diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index ba05e88c..bf5a3dc1 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -89,6 +89,9 @@ struct tablet_dispatch { struct libinput_timer prox_out_timer; bool proximity_out_forced; uint64_t last_event_time; + + /* true while injecting BTN_TOOL_PEN events */ + bool proximity_out_in_progress; } quirks; }; diff --git a/test/test-tablet.c b/test/test-tablet.c index 66b4f79c..92fd45b9 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -4445,9 +4445,95 @@ START_TEST(huion_static_btn_tool_pen_no_timeout_during_usage) } END_TEST +START_TEST(huion_static_btn_tool_pen_disable_quirk_on_prox_out) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + bool with_timeout = _i; /* ranged test */ + int i; + + /* test is run twice, once where the real BTN_TOOL_PEN is triggered + * during proximity out, one where the real BTN_TOOL_PEN is + * triggered after we already triggered the quirk timeout + */ + + litest_drain_events(li); + + litest_event(dev, EV_ABS, ABS_X, 20000); + litest_event(dev, EV_ABS, ABS_Y, 20000); + litest_event(dev, EV_ABS, ABS_PRESSURE, 100); + litest_event(dev, EV_KEY, BTN_TOOL_PEN, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_drain_events(li); + + for (i = 0; i < 10; i++) { + litest_event(dev, EV_ABS, ABS_X, 20000 + 10 * i); + litest_event(dev, EV_ABS, ABS_Y, 20000 - 10 * i); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + } + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_TABLET_TOOL_AXIS); + + /* Wait past the timeout to expect a proximity out */ + if (with_timeout) { + litest_timeout_tablet_proxout(); + libinput_dispatch(li); + litest_assert_tablet_proximity_event(li, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT); + } + + /* Send a real prox out, expect quirk to be disabled */ + litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + if (with_timeout) { + /* we got the proximity event above already */ + litest_assert_empty_queue(li); + } else { + litest_assert_tablet_proximity_event(li, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT); + } + + litest_push_event_frame(dev); + litest_tablet_proximity_out(dev); + litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_pop_event_frame(dev); + + litest_tablet_proximity_in(dev, 50, 50, NULL); + libinput_dispatch(li); + litest_assert_tablet_proximity_event(li, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); + libinput_dispatch(li); + + for (i = 0; i < 10; i++) { + litest_tablet_motion(dev, 50 + i, 50 + i, NULL); + libinput_dispatch(li); + } + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_TABLET_TOOL_AXIS); + + litest_push_event_frame(dev); + litest_tablet_proximity_out(dev); + litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_pop_event_frame(dev); + libinput_dispatch(li); + + litest_assert_tablet_proximity_event(li, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT); + litest_assert_empty_queue(li); +} +END_TEST + void litest_setup_tests_tablet(void) { + struct range with_timeout = { 0, 2 }; + litest_add("tablet:tool", tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add("tablet:tool", tool_user_data, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add("tablet:tool", tool_capability, LITEST_TABLET, LITEST_ANY); @@ -4543,4 +4629,5 @@ litest_setup_tests_tablet(void) litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen, LITEST_HUION_TABLET); litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen_no_timeout_during_usage, LITEST_HUION_TABLET); + litest_add_ranged_for_device("tablet:quirks", huion_static_btn_tool_pen_disable_quirk_on_prox_out, LITEST_HUION_TABLET, &with_timeout); } diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 867ffa53..6525b800 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -171,15 +171,10 @@ libinput:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnHP:pnHPZBookStudioG3:* # matching BTN_TOOL_PEN value 0 event. The device appears as if it was # permanently in proximity. # -# If the tablet is affected by this bug, copy the two lines below into a new -# file -# /etc/udev/hwdb.d/90-libinput-huion-pentablet-proximity-quirk.hwdb, then run -# sudo udevadm hwdb --update and reboot. -# -# Note that HUION re-uses USB IDs for its devices, not ever HUION tablet is -# affected by this bug. -#libinput:name:PenTablet Pen:dmi:* -# LIBINPUT_MODEL_TABLET_NO_PROXIMITY_OUT=1 +# HUION re-uses USB IDs for its devices, not every HUION tablet is +# affected by this bug, libinput will auto-disable this feature +libinput:tablet:input:b0003v256Cp* + LIBINPUT_MODEL_TABLET_NO_PROXIMITY_OUT=1 ########################################## # LENOVO diff --git a/udev/90-libinput-model-quirks.rules.in b/udev/90-libinput-model-quirks.rules.in index 08137812..e7d56bbe 100644 --- a/udev/90-libinput-model-quirks.rules.in +++ b/udev/90-libinput-model-quirks.rules.in @@ -29,6 +29,10 @@ KERNELS=="*input*", \ ENV{ID_INPUT_TOUCHPAD}=="1", \ IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:touchpad:" +# libinput:tablet: +ENV{ID_INPUT_TABLET}=="1", \ + IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:tablet:" + # libinput:mouse: ENV{ID_INPUT_MOUSE}=="1", \ IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:mouse:" diff --git a/udev/parse_hwdb.py b/udev/parse_hwdb.py index f6b9ec0a..69eb65b0 100755 --- a/udev/parse_hwdb.py +++ b/udev/parse_hwdb.py @@ -59,7 +59,7 @@ REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER)) UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_') TYPES = { - 'libinput': ('name', 'touchpad', 'mouse', 'keyboard'), + 'libinput': ('name', 'touchpad', 'mouse', 'keyboard', 'tablet'), }