From d20bbfa5cb56e8bc47da4a7a9ff8fe548514d2ac Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 30 Oct 2019 15:23:24 +1000 Subject: [PATCH] tablet: handle a direct tool switch correctly Fixes #259 Signed-off-by: Peter Hutterer --- src/evdev-tablet.c | 37 +++++++++++++++++++++++--------- test/test-tablet.c | 53 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index f2fbf847b..67880d037 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1731,7 +1731,7 @@ tablet_proximity_out_quirk_set_timer(struct tablet_dispatch *tablet, time + FORCED_PROXOUT_TIMEOUT); } -static void +static bool tablet_update_tool_state(struct tablet_dispatch *tablet, struct evdev_device *device, uint64_t time) @@ -1739,6 +1739,7 @@ tablet_update_tool_state(struct tablet_dispatch *tablet, enum libinput_tablet_tool_type type; uint32_t changed; int state; + uint32_t doubled_up_new_tool_bit = 0; /* We need to emulate a BTN_TOOL_PEN if we get an axis event (i.e. * stylus is def. in proximity) and: @@ -1766,16 +1767,23 @@ tablet_update_tool_state(struct tablet_dispatch *tablet, } if (tablet->tool_state == tablet->prev_tool_state) - return; + return false; /* Kernel tools are supposed to be mutually exclusive, if we have - * two set discard the most recent one. */ + * two, we force a proximity out for the older tool and handle the + * new tool as separate proximity in event. + */ if (tablet->tool_state & (tablet->tool_state - 1)) { - evdev_log_bug_kernel(device, - "Multiple tools active simultaneously (%#x)\n", - tablet->tool_state); - tablet->tool_state = tablet->prev_tool_state; - goto out; + /* tool_state has 2 bits set. We set the current tool state + * to zero, thus setting everything up for a prox out on the + * tool. Once that is set up, we change the tool state to be + * the new one we just got so when we re-process this + * function we now get the new tool as prox in. + * Importantly, we basically rely on nothing else happening + * in the meantime. + */ + doubled_up_new_tool_bit = tablet->tool_state ^ tablet->prev_tool_state; + tablet->tool_state = 0; } changed = tablet->tool_state ^ tablet->prev_tool_state; @@ -1803,9 +1811,13 @@ tablet_update_tool_state(struct tablet_dispatch *tablet, } } -out: tablet->prev_tool_state = tablet->tool_state; + if (doubled_up_new_tool_bit) { + tablet->tool_state = doubled_up_new_tool_bit; + return true; /* need to re-process */ + } + return false; } static struct libinput_tablet_tool * @@ -1826,8 +1838,10 @@ tablet_flush(struct tablet_dispatch *tablet, uint64_t time) { struct libinput_tablet_tool *tool; + bool process_tool_twice; - tablet_update_tool_state(tablet, device, time); +reprocess: + process_tool_twice = tablet_update_tool_state(tablet, device, time); tool = tablet_get_current_tool(tablet); if (!tool) @@ -1860,6 +1874,9 @@ tablet_flush(struct tablet_dispatch *tablet, } tablet_send_events(tablet, tool, device, time); + + if (process_tool_twice) + goto reprocess; } static inline void diff --git a/test/test-tablet.c b/test/test-tablet.c index 2ff3eae35..41452eeef 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -2902,12 +2902,28 @@ START_TEST(tool_direct_switch_skip_tool_update) libinput_tablet_tool_ref(tool); libinput_event_destroy(event); - /* Direct tool switch after proximity in is ignored */ - litest_disable_log_handler(li); litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_assert_empty_queue(li); - litest_restore_log_handler(li); + libinput_dispatch(li); + + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev), + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT); + ck_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), tool); + libinput_event_destroy(event); + + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev), + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); + ck_assert_ptr_ne(libinput_event_tablet_tool_get_tool(tev), tool); + libinput_tablet_tool_unref(tool); + tool = libinput_event_tablet_tool_get_tool(tev); + libinput_tablet_tool_ref(tool); + libinput_event_destroy(event); litest_tablet_motion(dev, 20, 30, axes); libinput_dispatch(li); @@ -2919,18 +2935,36 @@ START_TEST(tool_direct_switch_skip_tool_update) tool); libinput_event_destroy(event); - /* Direct tool switch during sequence in is ignored */ - litest_disable_log_handler(li); litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0); litest_event(dev, EV_SYN, SYN_REPORT, 0); - litest_assert_empty_queue(li); + libinput_dispatch(li); + + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev), + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT); + ck_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), + tool); + libinput_event_destroy(event); litest_push_event_frame(dev); litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1); litest_tablet_motion(dev, 30, 40, axes); litest_pop_event_frame(dev); libinput_dispatch(li); - litest_restore_log_handler(li); + + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + ck_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev), + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); + ck_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), + tool); + libinput_event_destroy(event); + + litest_tablet_motion(dev, 40, 30, axes); + libinput_dispatch(li); event = libinput_get_event(li); tev = litest_is_tablet_event(event, @@ -2939,7 +2973,10 @@ START_TEST(tool_direct_switch_skip_tool_update) tool); libinput_event_destroy(event); + litest_push_event_frame(dev); + litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0); litest_tablet_proximity_out(dev); + litest_pop_event_frame(dev); libinput_dispatch(li); litest_timeout_tablet_proxout(); libinput_dispatch(li);