mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-22 23:00:05 +01:00
tablet: handle custom proximity handling
For the puck/lens cursor tool we need to artificially reduce proximity
detection. These tools are usually used in a relative mode (i.e. like a mouse)
and thus require lifting and resetting the tool multiple times to move across
the screen. The tablets' distance detection goes too far, requiring the user
to lift the device several cm on every move. This is uncomfortable.
Introduce an artificial distance threshold for the devices with the default
value taken from the X.Org wacom driver. If a tool is in proximity but outside
of this range, fake proximity events accordingly.
If a button was pressed while we were out of range we discard that event and
send it later when we enter proximity again.
This is the simple implementation that only takes one proximity out value (the
one from the wacom driver) and applies it to all. Those devices that support a
button/lens tool and have a different default threshold are well out of date.
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
[rebased, tests updated for new axis percentage behavior (8d76734f)]
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
c124209cad
commit
2f481ba4a2
5 changed files with 348 additions and 2 deletions
|
|
@ -68,6 +68,22 @@ tablet_get_released_buttons(struct tablet_dispatch *tablet,
|
|||
~(state->stylus_buttons[i]);
|
||||
}
|
||||
|
||||
/* Merge the previous state with the current one so all buttons look like
|
||||
* they just got pressed in this frame */
|
||||
static inline void
|
||||
tablet_force_button_presses(struct tablet_dispatch *tablet)
|
||||
{
|
||||
struct button_state *state = &tablet->button_state,
|
||||
*prev_state = &tablet->prev_button_state;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(state->stylus_buttons); i++) {
|
||||
state->stylus_buttons[i] = state->stylus_buttons[i] |
|
||||
prev_state->stylus_buttons[i];
|
||||
prev_state->stylus_buttons[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tablet_device_has_axis(struct tablet_dispatch *tablet,
|
||||
enum libinput_tablet_tool_axis axis)
|
||||
|
|
@ -1063,6 +1079,63 @@ tablet_mark_all_axes_changed(struct tablet_dispatch *tablet,
|
|||
sizeof(tablet->changed_axes));
|
||||
}
|
||||
|
||||
static void
|
||||
tablet_update_proximity_state(struct tablet_dispatch *tablet,
|
||||
struct evdev_device *device,
|
||||
struct libinput_tablet_tool *tool)
|
||||
{
|
||||
const struct input_absinfo *distance;
|
||||
int dist_max = tablet->cursor_proximity_threshold;
|
||||
int dist;
|
||||
|
||||
distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
|
||||
if (!distance)
|
||||
return;
|
||||
|
||||
dist = distance->value;
|
||||
if (dist == 0)
|
||||
return;
|
||||
|
||||
/* Tool got into permitted range */
|
||||
if (dist < dist_max &&
|
||||
(tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
|
||||
tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))) {
|
||||
tablet_unset_status(tablet,
|
||||
TABLET_TOOL_OUT_OF_RANGE);
|
||||
tablet_unset_status(tablet,
|
||||
TABLET_TOOL_OUT_OF_PROXIMITY);
|
||||
tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
|
||||
tablet_mark_all_axes_changed(tablet, tool);
|
||||
|
||||
tablet_set_status(tablet, TABLET_BUTTONS_PRESSED);
|
||||
tablet_force_button_presses(tablet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dist < dist_max)
|
||||
return;
|
||||
|
||||
/* Still out of range/proximity */
|
||||
if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
|
||||
tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
|
||||
return;
|
||||
|
||||
/* Tool entered prox but is outside of permitted range */
|
||||
if (tablet_has_status(tablet,
|
||||
TABLET_TOOL_ENTERING_PROXIMITY)) {
|
||||
tablet_set_status(tablet,
|
||||
TABLET_TOOL_OUT_OF_RANGE);
|
||||
tablet_unset_status(tablet,
|
||||
TABLET_TOOL_ENTERING_PROXIMITY);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Tool was in prox and is now outside of range. Set leaving
|
||||
* proximity, on the next event it will be OUT_OF_PROXIMITY and thus
|
||||
* caught by the above conditions */
|
||||
tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
|
||||
}
|
||||
|
||||
static void
|
||||
tablet_flush(struct tablet_dispatch *tablet,
|
||||
struct evdev_device *device,
|
||||
|
|
@ -1074,7 +1147,12 @@ tablet_flush(struct tablet_dispatch *tablet,
|
|||
tablet->current_tool_id,
|
||||
tablet->current_tool_serial);
|
||||
|
||||
if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
|
||||
if (tool->type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
|
||||
tool->type == LIBINPUT_TABLET_TOOL_TYPE_LENS)
|
||||
tablet_update_proximity_state(tablet, device, tool);
|
||||
|
||||
if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY) ||
|
||||
tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE))
|
||||
return;
|
||||
|
||||
if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
|
||||
|
|
@ -1271,6 +1349,30 @@ tablet_init_calibration(struct tablet_dispatch *tablet,
|
|||
evdev_init_calibration(device, &tablet->base);
|
||||
}
|
||||
|
||||
static void
|
||||
tablet_init_proximity_threshold(struct tablet_dispatch *tablet,
|
||||
struct evdev_device *device)
|
||||
{
|
||||
/* This rules out most of the bamboos and other devices, we're
|
||||
* pretty much down to
|
||||
*/
|
||||
if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_MOUSE) &&
|
||||
!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_LENS))
|
||||
return;
|
||||
|
||||
/* 42 is the default proximity threshold the xf86-input-wacom driver
|
||||
* uses for Intuos/Cintiq models. Graphire models have a threshold
|
||||
* of 10 but since they haven't been manufactured in ages and the
|
||||
* intersection of users having a graphire, running libinput and
|
||||
* wanting to use the mouse/lens cursor tool is small enough to not
|
||||
* worry about it for now. If we need to, we can introduce a udev
|
||||
* property later.
|
||||
*
|
||||
* Value is in device coordinates.
|
||||
*/
|
||||
tablet->cursor_proximity_threshold = 42;
|
||||
}
|
||||
|
||||
static int
|
||||
tablet_init(struct tablet_dispatch *tablet,
|
||||
struct evdev_device *device)
|
||||
|
|
@ -1284,6 +1386,7 @@ tablet_init(struct tablet_dispatch *tablet,
|
|||
list_init(&tablet->tool_list);
|
||||
|
||||
tablet_init_calibration(tablet, device);
|
||||
tablet_init_proximity_threshold(tablet, device);
|
||||
|
||||
for (axis = LIBINPUT_TABLET_TOOL_AXIS_X;
|
||||
axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ enum tablet_status {
|
|||
TABLET_TOOL_ENTERING_PROXIMITY = 1 << 6,
|
||||
TABLET_TOOL_ENTERING_CONTACT = 1 << 7,
|
||||
TABLET_TOOL_LEAVING_CONTACT = 1 << 8,
|
||||
TABLET_TOOL_OUT_OF_RANGE = 1 << 9,
|
||||
};
|
||||
|
||||
struct button_state {
|
||||
|
|
@ -65,6 +66,8 @@ struct tablet_dispatch {
|
|||
enum libinput_tablet_tool_type current_tool_type;
|
||||
uint32_t current_tool_id;
|
||||
uint32_t current_tool_serial;
|
||||
|
||||
uint32_t cursor_proximity_threshold;
|
||||
};
|
||||
|
||||
static inline enum libinput_tablet_tool_axis
|
||||
|
|
|
|||
|
|
@ -2403,6 +2403,24 @@ litest_assert_tablet_button_event(struct libinput *li, unsigned int button,
|
|||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
void litest_assert_tablet_proximity_event(struct libinput *li,
|
||||
enum libinput_tablet_tool_proximity_state state)
|
||||
{
|
||||
struct libinput_event *event;
|
||||
struct libinput_event_tablet_tool *tev;
|
||||
enum libinput_event_type type = LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY;
|
||||
|
||||
litest_wait_for_event(li);
|
||||
event = libinput_get_event(li);
|
||||
|
||||
litest_assert_notnull(event);
|
||||
litest_assert_int_eq(libinput_event_get_type(event), type);
|
||||
tev = libinput_event_get_tablet_tool_event(event);
|
||||
litest_assert_int_eq(libinput_event_tablet_tool_get_proximity_state(tev),
|
||||
state);
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
|
||||
void
|
||||
litest_assert_scroll(struct libinput *li,
|
||||
enum libinput_pointer_axis axis,
|
||||
|
|
|
|||
|
|
@ -457,7 +457,8 @@ void litest_assert_only_typed_events(struct libinput *li,
|
|||
void litest_assert_tablet_button_event(struct libinput *li,
|
||||
unsigned int button,
|
||||
enum libinput_button_state state);
|
||||
|
||||
void litest_assert_tablet_proximity_event(struct libinput *li,
|
||||
enum libinput_tablet_tool_proximity_state state);
|
||||
struct libevdev_uinput * litest_create_uinput_device(const char *name,
|
||||
struct input_id *id,
|
||||
...);
|
||||
|
|
|
|||
221
test/tablet.c
221
test/tablet.c
|
|
@ -811,6 +811,222 @@ START_TEST(proximity_has_axes)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(proximity_range_enter)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct axis_replacement axes[] = {
|
||||
{ ABS_DISTANCE, 90 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
if (!libevdev_has_event_code(dev->evdev,
|
||||
EV_KEY,
|
||||
BTN_TOOL_MOUSE))
|
||||
return;
|
||||
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_push_event_frame(dev);
|
||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||
litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
|
||||
litest_pop_event_frame(dev);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
axes[0].value = 20;
|
||||
litest_tablet_motion(dev, 10, 10, axes);
|
||||
libinput_dispatch(li);
|
||||
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
|
||||
axes[0].value = 90;
|
||||
litest_tablet_motion(dev, 10, 10, axes);
|
||||
libinput_dispatch(li);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
|
||||
|
||||
litest_tablet_proximity_out(dev);
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(proximity_range_in_out)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct axis_replacement axes[] = {
|
||||
{ ABS_DISTANCE, 20 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
if (!libevdev_has_event_code(dev->evdev,
|
||||
EV_KEY,
|
||||
BTN_TOOL_MOUSE))
|
||||
return;
|
||||
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_push_event_frame(dev);
|
||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||
litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
|
||||
litest_pop_event_frame(dev);
|
||||
libinput_dispatch(li);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
|
||||
|
||||
axes[0].value = 90;
|
||||
litest_tablet_motion(dev, 10, 10, axes);
|
||||
libinput_dispatch(li);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
|
||||
|
||||
litest_tablet_motion(dev, 30, 30, axes);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
axes[0].value = 20;
|
||||
litest_tablet_motion(dev, 10, 10, axes);
|
||||
libinput_dispatch(li);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
|
||||
|
||||
litest_tablet_proximity_out(dev);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(proximity_range_button_click)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct axis_replacement axes[] = {
|
||||
{ ABS_DISTANCE, 90 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
if (!libevdev_has_event_code(dev->evdev,
|
||||
EV_KEY,
|
||||
BTN_TOOL_MOUSE))
|
||||
return;
|
||||
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_push_event_frame(dev);
|
||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||
litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
|
||||
litest_pop_event_frame(dev);
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 1);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
libinput_dispatch(li);
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 0);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
libinput_dispatch(li);
|
||||
|
||||
litest_tablet_proximity_out(dev);
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(proximity_range_button_press)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct axis_replacement axes[] = {
|
||||
{ ABS_DISTANCE, 20 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
if (!libevdev_has_event_code(dev->evdev,
|
||||
EV_KEY,
|
||||
BTN_TOOL_MOUSE))
|
||||
return;
|
||||
|
||||
litest_push_event_frame(dev);
|
||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||
litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
|
||||
litest_pop_event_frame(dev);
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 1);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
libinput_dispatch(li);
|
||||
|
||||
litest_assert_tablet_button_event(li,
|
||||
BTN_STYLUS,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
|
||||
axes[0].value = 90;
|
||||
litest_tablet_motion(dev, 15, 15, axes);
|
||||
libinput_dispatch(li);
|
||||
|
||||
/* expect fake button release */
|
||||
litest_assert_tablet_button_event(li,
|
||||
BTN_STYLUS,
|
||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
|
||||
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 0);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
libinput_dispatch(li);
|
||||
|
||||
litest_tablet_proximity_out(dev);
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(proximity_range_button_release)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
struct libinput *li = dev->libinput;
|
||||
struct axis_replacement axes[] = {
|
||||
{ ABS_DISTANCE, 90 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
if (!libevdev_has_event_code(dev->evdev,
|
||||
EV_KEY,
|
||||
BTN_TOOL_MOUSE))
|
||||
return;
|
||||
|
||||
litest_push_event_frame(dev);
|
||||
litest_tablet_proximity_in(dev, 10, 10, axes);
|
||||
litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
|
||||
litest_pop_event_frame(dev);
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 1);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
axes[0].value = 20;
|
||||
litest_tablet_motion(dev, 15, 15, axes);
|
||||
libinput_dispatch(li);
|
||||
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_IN);
|
||||
/* expect fake button press */
|
||||
litest_assert_tablet_button_event(li,
|
||||
BTN_STYLUS,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
litest_event(dev, EV_KEY, BTN_STYLUS, 0);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
libinput_dispatch(li);
|
||||
litest_assert_tablet_button_event(li,
|
||||
BTN_STYLUS,
|
||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||
|
||||
litest_tablet_proximity_out(dev);
|
||||
litest_assert_tablet_proximity_event(li,
|
||||
LIBINPUT_TABLET_TOOL_PROXIMITY_OUT);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(motion)
|
||||
{
|
||||
struct litest_device *dev = litest_current_device();
|
||||
|
|
@ -2872,6 +3088,11 @@ litest_setup_tests(void)
|
|||
litest_add("tablet:proximity", proximity_in_out, LITEST_TABLET, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_has_axes, LITEST_TABLET, LITEST_ANY);
|
||||
litest_add("tablet:proximity", bad_distance_events, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_range_enter, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_range_in_out, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_range_button_click, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_range_button_press, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:proximity", proximity_range_button_release, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
|
||||
litest_add("tablet:tip", tip_down_up, LITEST_TABLET, LITEST_ANY);
|
||||
litest_add("tablet:tip", tip_down_prox_in, LITEST_TABLET, LITEST_ANY);
|
||||
litest_add("tablet:tip", tip_up_prox_out, LITEST_TABLET, LITEST_ANY);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue