Merge branch 'touchpad-pressure-based-v2'

This commit is contained in:
Peter Hutterer 2017-02-03 11:41:04 +10:00
commit 00d6b908d3
11 changed files with 804 additions and 242 deletions

View file

@ -766,6 +766,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
if (t->tap.is_thumb)
continue;
if (t->state == TOUCH_HOVERING)
continue;
if (t->state == TOUCH_BEGIN) {
/* The simple version: if a touch is a thumb on
* begin we ignore it. All other thumb touches
@ -787,7 +790,8 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
} else if (t->state == TOUCH_END) {
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
if (t->was_down)
tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
t->tap.state = TAP_TOUCH_STATE_IDLE;
} else if (tp->tap.state != TAP_STATE_IDLE &&
tp_tap_exceeds_motion_threshold(tp, t)) {

View file

@ -214,6 +214,7 @@ tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
tp_motion_history_reset(t);
t->dirty = true;
t->has_ended = false;
t->was_down = false;
t->state = TOUCH_HOVERING;
t->pinned.is_pinned = false;
t->millis = time;
@ -226,6 +227,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
t->dirty = true;
t->state = TOUCH_BEGIN;
t->millis = time;
t->was_down = true;
tp->nfingers_down++;
t->palm.time = time;
t->thumb.state = THUMB_STATE_MAYBE;
@ -320,9 +322,6 @@ tp_process_absolute(struct tp_dispatch *tp,
case ABS_MT_SLOT:
tp->slot = e->value;
break;
case ABS_MT_DISTANCE:
t->distance = e->value;
break;
case ABS_MT_TRACKING_ID:
if (e->value != -1)
tp_new_touch(tp, t, time);
@ -363,6 +362,11 @@ tp_process_absolute_st(struct tp_dispatch *tp,
t->dirty = true;
tp->queued |= TOUCHPAD_EVENT_MOTION;
break;
case ABS_PRESSURE:
t->pressure = e->value;
t->dirty = true;
tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
break;
}
}
@ -793,26 +797,72 @@ out:
}
static void
tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
tp_unhover_pressure(struct tp_dispatch *tp, uint64_t time)
{
struct tp_touch *t;
unsigned int i;
int i;
unsigned int nfake_touches;
unsigned int real_fingers_down = 0;
for (i = 0; i < tp->ntouches; i++) {
nfake_touches = tp_fake_finger_count(tp);
if (nfake_touches == FAKE_FINGER_OVERFLOW)
nfake_touches = 0;
for (i = 0; i < (int)tp->num_slots; i++) {
t = tp_get_touch(tp, i);
if (!t->dirty)
continue;
if (t->state == TOUCH_HOVERING) {
if (t->distance == 0) {
/* avoid jumps when landing a finger */
tp_motion_history_reset(t);
tp_begin_touch(tp, t, time);
if (t->dirty) {
if (t->state == TOUCH_HOVERING) {
if (t->pressure >= tp->pressure.high) {
/* avoid jumps when landing a finger */
tp_motion_history_reset(t);
tp_begin_touch(tp, t, time);
}
} else {
if (t->pressure < tp->pressure.low)
tp_end_touch(tp, t, time);
}
} else {
if (t->distance > 0)
tp_end_touch(tp, t, time);
}
if (t->state == TOUCH_BEGIN ||
t->state == TOUCH_UPDATE)
real_fingers_down++;
}
if (nfake_touches <= tp->num_slots ||
tp->nfingers_down == 0)
return;
/* if we have more fake fingers down than slots, we assume
* _all_ fingers have enough pressure, even if some of the slotted
* ones don't. Anything else gets insane quickly.
*/
for (i = 0; i < (int)tp->ntouches; i++) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_HOVERING) {
/* avoid jumps when landing a finger */
tp_motion_history_reset(t);
tp_begin_touch(tp, t, time);
if (tp->nfingers_down >= nfake_touches)
break;
}
}
if (tp->nfingers_down > nfake_touches ||
real_fingers_down == 0) {
for (i = tp->ntouches - 1; i >= 0; i--) {
t = tp_get_touch(tp, i);
if (t->state == TOUCH_HOVERING ||
t->state == TOUCH_NONE)
continue;
tp_end_touch(tp, t, time);
if (real_fingers_down > 0 &&
tp->nfingers_down == nfake_touches)
break;
}
}
}
@ -879,8 +929,8 @@ tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time)
static void
tp_unhover_touches(struct tp_dispatch *tp, uint64_t time)
{
if (tp->reports_distance)
tp_unhover_abs_distance(tp, time);
if (tp->pressure.use_pressure)
tp_unhover_pressure(tp, time);
else
tp_unhover_fake_touches(tp, time);
@ -924,6 +974,7 @@ tp_position_fake_touches(struct tp_dispatch *tp)
continue;
t->point = topmost->point;
t->pressure = topmost->pressure;
if (!t->dirty)
t->dirty = topmost->dirty;
}
@ -1791,7 +1842,13 @@ tp_sync_touch(struct tp_dispatch *tp,
&t->point.y))
t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
libevdev_fetch_slot_value(evdev, slot, ABS_MT_DISTANCE, &t->distance);
if (!libevdev_fetch_slot_value(evdev,
slot,
ABS_MT_PRESSURE,
&t->pressure))
t->pressure = libevdev_get_event_value(evdev,
EV_ABS,
ABS_PRESSURE);
}
static inline void
@ -2313,6 +2370,38 @@ tp_init_hysteresis(struct tp_dispatch *tp)
tp->hysteresis_margin.y = res_y/2;
}
static void
tp_init_pressure(struct tp_dispatch *tp,
struct evdev_device *device)
{
const struct input_absinfo *abs;
unsigned int range;
unsigned int code = ABS_PRESSURE;
if (tp->has_mt)
code = ABS_MT_PRESSURE;
if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) {
tp->pressure.use_pressure = false;
return;
}
tp->pressure.use_pressure = true;
abs = libevdev_get_abs_info(device->evdev, code);
assert(abs);
range = abs->maximum - abs->minimum;
/* Approximately the synaptics defaults */
tp->pressure.high = abs->minimum + 0.12 * range;
tp->pressure.low = abs->minimum + 0.10 * range;
log_debug(evdev_libinput_context(device),
"%s: using pressure-based touch detection\n",
device->devname);
}
static int
tp_init(struct tp_dispatch *tp,
struct evdev_device *device)
@ -2331,9 +2420,7 @@ tp_init(struct tp_dispatch *tp,
evdev_device_init_abs_range_warnings(device);
tp->reports_distance = libevdev_has_event_code(device->evdev,
EV_ABS,
ABS_MT_DISTANCE);
tp_init_pressure(tp, device);
/* Set the dpi to that of the x axis, because that's what we normalize
to when needed*/

View file

@ -153,9 +153,11 @@ struct tp_touch {
bool dirty;
struct device_coords point;
uint64_t millis;
int distance; /* distance == 0 means touch */
int pressure;
bool was_down; /* if distance == 0, false for pure hovering
touches */
struct {
/* A quirk mostly used on Synaptics touchpads. In a
transition to/from fake touches > num_slots, the current
@ -229,7 +231,6 @@ struct tp_dispatch {
unsigned int slot; /* current slot */
bool has_mt;
bool semi_mt;
bool reports_distance; /* does the device support true hovering */
/* true if we're reading events (i.e. not suspended) but we're
* ignoring them */
@ -245,6 +246,14 @@ struct tp_dispatch {
*/
unsigned int fake_touches;
/* if pressure goes above high -> touch down,
if pressure then goes below low -> touch up */
struct {
bool use_pressure;
int high;
int low;
} pressure;
struct device_coords hysteresis_margin;
struct {

View file

@ -30,8 +30,6 @@
#include "litest.h"
#include "litest-int.h"
static void alps_dualpoint_create(struct litest_device *d);
static void
litest_alps_dualpoint_setup(void)
{
@ -39,34 +37,46 @@ litest_alps_dualpoint_setup(void)
litest_set_current_device(d);
}
static void
alps_dualpoint_touch_down(struct litest_device *d, unsigned int slot, double x, double y)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_down(d, semi_mt, slot, x, y);
}
static void
alps_dualpoint_touch_move(struct litest_device *d, unsigned int slot, double x, double y)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_move(d, semi_mt, slot, x, y);
}
static void
alps_dualpoint_touch_up(struct litest_device *d, unsigned int slot)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_up(d, semi_mt, slot);
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down = alps_dualpoint_touch_down,
.touch_move = alps_dualpoint_touch_move,
.touch_up = alps_dualpoint_touch_up,
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {
@ -116,7 +126,6 @@ struct litest_test_device litest_alps_dualpoint_device = {
.shortname = "alps dualpoint",
.setup = litest_alps_dualpoint_setup,
.interface = &interface,
.create = alps_dualpoint_create,
.name = "AlpsPS/2 ALPS DualPoint TouchPad",
.id = &input_id,
@ -124,18 +133,3 @@ struct litest_test_device litest_alps_dualpoint_device = {
.absinfo = absinfo,
.udev_rule = udev_rule,
};
static void
alps_dualpoint_create(struct litest_device *d)
{
struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
assert(semi_mt);
d->private = semi_mt;
d->uinput = litest_create_uinput_device_from_description(litest_alps_dualpoint_device.name,
litest_alps_dualpoint_device.id,
absinfo,
events);
d->interface = &interface;
}

View file

@ -30,8 +30,6 @@
#include "litest.h"
#include "litest-int.h"
static void alps_create(struct litest_device *d);
static void
litest_alps_setup(void)
{
@ -39,34 +37,46 @@ litest_alps_setup(void)
litest_set_current_device(d);
}
static void
alps_touch_down(struct litest_device *d, unsigned int slot, double x, double y)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_down(d, semi_mt, slot, x, y);
}
static void
alps_touch_move(struct litest_device *d, unsigned int slot, double x, double y)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_move(d, semi_mt, slot, x, y);
}
static void
alps_touch_up(struct litest_device *d, unsigned int slot)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_up(d, semi_mt, slot);
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down = alps_touch_down,
.touch_move = alps_touch_move,
.touch_up = alps_touch_up,
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {
@ -106,25 +116,9 @@ struct litest_test_device litest_alps_device = {
.shortname = "alps semi-mt",
.setup = litest_alps_setup,
.interface = &interface,
.create = alps_create,
.name = "AlpsPS/2 ALPS GlidePoint",
.id = &input_id,
.events = events,
.absinfo = absinfo,
};
static void
alps_create(struct litest_device *d)
{
struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
assert(semi_mt);
d->private = semi_mt;
d->uinput = litest_create_uinput_device_from_description(litest_alps_device.name,
litest_alps_device.id,
absinfo,
events);
d->interface = &interface;
}

View file

@ -76,10 +76,24 @@ static struct input_event up[] = {
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.touch_up_events = up,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {

View file

@ -30,9 +30,6 @@
#include "litest.h"
#include "litest-int.h"
static void
synaptics_hover_create(struct litest_device *d);
static void
litest_synaptics_hover_setup(void)
{
@ -40,34 +37,46 @@ litest_synaptics_hover_setup(void)
litest_set_current_device(d);
}
static void
synaptics_hover_touch_down(struct litest_device *d, unsigned int slot, double x, double y)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static struct input_event move[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_down(d, semi_mt, slot, x, y);
}
static void
synaptics_hover_touch_move(struct litest_device *d, unsigned int slot, double x, double y)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_move(d, semi_mt, slot, x, y);
}
static void
synaptics_hover_touch_up(struct litest_device *d, unsigned int slot)
{
struct litest_semi_mt *semi_mt = d->private;
litest_semi_mt_touch_up(d, semi_mt, slot);
switch (evcode) {
case ABS_PRESSURE:
case ABS_MT_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down = synaptics_hover_touch_down,
.touch_move = synaptics_hover_touch_move,
.touch_up = synaptics_hover_touch_up,
.touch_down_events = down,
.touch_move_events = move,
.get_axis_default = get_axis_default,
};
static struct input_id input_id = {
@ -115,7 +124,6 @@ struct litest_test_device litest_synaptics_hover_device = {
.shortname = "synaptics hover",
.setup = litest_synaptics_hover_setup,
.interface = &interface,
.create = synaptics_hover_create,
.name = "SynPS/2 Synaptics TouchPad",
.id = &input_id,
@ -123,19 +131,3 @@ struct litest_test_device litest_synaptics_hover_device = {
.absinfo = absinfo,
.udev_rule = udev_rule,
};
static void
synaptics_hover_create(struct litest_device *d)
{
struct litest_semi_mt *semi_mt = zalloc(sizeof(*semi_mt));
assert(semi_mt);
d->private = semi_mt;
d->uinput = litest_create_uinput_device_from_description(
litest_synaptics_hover_device.name,
litest_synaptics_hover_device.id,
absinfo,
events);
d->interface = &interface;
}

View file

@ -36,7 +36,7 @@ litest_synaptics_touchpad_setup(void)
static struct input_event down[] = {
{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 30 },
{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_TOOL_WIDTH, .value = 7 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 },
@ -54,10 +54,23 @@ struct input_event up[] = {
{ .type = -1, .code = -1 },
};
static int
get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
{
switch (evcode) {
case ABS_PRESSURE:
*value = 30;
return 0;
}
return 1;
}
static struct litest_device_interface interface = {
.touch_down_events = down,
.touch_move_events = move,
.touch_up_events = up,
.get_axis_default = get_axis_default,
};
static struct input_absinfo absinfo[] = {

View file

@ -1206,7 +1206,7 @@ litest_create(enum litest_device_type which,
const char *name;
const struct input_id *id;
struct input_absinfo *abs;
int *events;
int *events, *e;
dev = devices;
while (*dev) {
@ -1241,6 +1241,18 @@ litest_create(enum litest_device_type which,
abs,
events);
d->interface = (*dev)->interface;
for (e = events; *e != -1; e += 2) {
unsigned int type = *e,
code = *(e + 1);
if (type == INPUT_PROP_MAX &&
code == INPUT_PROP_SEMI_MT) {
d->semi_mt.is_semi_mt = true;
break;
}
}
free(abs);
free(events);
@ -1391,6 +1403,8 @@ litest_delete_device(struct litest_device *d)
if (!d)
return;
litest_assert_int_eq(d->skip_ev_syn, 0);
libinput_device_unref(d->libinput_device);
libinput_path_remove_device(d->libinput_device);
if (d->owns_context)
@ -1491,12 +1505,13 @@ send_btntool(struct litest_device *d, bool hover)
}
static void
litest_slot_start(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching)
slot_start(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching,
bool filter_abs_xy)
{
struct input_event *ev;
@ -1510,8 +1525,9 @@ litest_slot_start(struct litest_device *d,
return;
}
ev = d->interface->touch_down_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
for (ev = d->interface->touch_down_events;
ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
ev++) {
int value = litest_auto_assign_value(d,
ev,
slot,
@ -1519,12 +1535,142 @@ litest_slot_start(struct litest_device *d,
y,
axes,
touching);
if (value != LITEST_AUTO_ASSIGN)
litest_event(d, ev->type, ev->code, value);
ev++;
if (value == LITEST_AUTO_ASSIGN)
continue;
if (filter_abs_xy && ev->type == EV_ABS &&
(ev->code == ABS_X || ev->code == ABS_Y))
continue;
litest_event(d, ev->type, ev->code, value);
}
}
static void
slot_move(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching,
bool filter_abs_xy)
{
struct input_event *ev;
if (d->interface->touch_move) {
d->interface->touch_move(d, slot, x, y);
return;
}
for (ev = d->interface->touch_move_events;
ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
ev++) {
int value = litest_auto_assign_value(d,
ev,
slot,
x,
y,
axes,
touching);
if (value == LITEST_AUTO_ASSIGN)
continue;
if (filter_abs_xy && ev->type == EV_ABS &&
(ev->code == ABS_X || ev->code == ABS_Y))
continue;
litest_event(d, ev->type, ev->code, value);
}
}
static void
touch_up(struct litest_device *d, unsigned int slot)
{
struct input_event *ev;
struct input_event up[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 }
};
litest_assert_int_gt(d->ntouches_down, 0);
d->ntouches_down--;
send_btntool(d, false);
if (d->interface->touch_up) {
d->interface->touch_up(d, slot);
return;
} else if (d->interface->touch_up_events) {
ev = d->interface->touch_up_events;
} else
ev = up;
for ( /* */;
ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
ev++) {
int value = litest_auto_assign_value(d,
ev,
slot,
0,
0,
NULL,
false);
litest_event(d, ev->type, ev->code, value);
}
}
static void
litest_slot_start(struct litest_device *d,
unsigned int slot,
double x,
double y,
struct axis_replacement *axes,
bool touching)
{
double t, l, r = 0, b = 0; /* top, left, right, bottom */
bool filter_abs_xy = false;
if (!d->semi_mt.is_semi_mt) {
slot_start(d, slot, x, y, axes, touching, filter_abs_xy);
return;
}
if (d->ntouches_down >= 2 || slot > 1)
return;
slot = d->ntouches_down;
if (d->ntouches_down == 0) {
l = x;
t = y;
} else {
int other = (slot + 1) % 2;
l = min(x, d->semi_mt.touches[other].x);
t = min(y, d->semi_mt.touches[other].y);
r = max(x, d->semi_mt.touches[other].x);
b = max(y, d->semi_mt.touches[other].y);
}
litest_push_event_frame(d);
if (d->ntouches_down == 0)
slot_start(d, 0, l, t, axes, touching, filter_abs_xy);
else
slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
if (slot == 1) {
filter_abs_xy = true;
slot_start(d, 1, r, b, axes, touching, filter_abs_xy);
}
litest_pop_event_frame(d);
d->semi_mt.touches[slot].x = x;
d->semi_mt.touches[slot].y = y;
}
void
litest_touch_down(struct litest_device *d,
unsigned int slot,
@ -1544,43 +1690,6 @@ litest_touch_down_extended(struct litest_device *d,
litest_slot_start(d, slot, x, y, axes, true);
}
void
litest_touch_up(struct litest_device *d, unsigned int slot)
{
struct input_event *ev;
struct input_event up[] = {
{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
{ .type = -1, .code = -1 }
};
litest_assert_int_gt(d->ntouches_down, 0);
d->ntouches_down--;
send_btntool(d, false);
if (d->interface->touch_up) {
d->interface->touch_up(d, slot);
return;
} else if (d->interface->touch_up_events) {
ev = d->interface->touch_up_events;
} else
ev = up;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
int value = litest_auto_assign_value(d,
ev,
slot,
0,
0,
NULL,
false);
litest_event(d, ev->type, ev->code, value);
ev++;
}
}
static void
litest_slot_move(struct litest_device *d,
unsigned int slot,
@ -1589,26 +1698,73 @@ litest_slot_move(struct litest_device *d,
struct axis_replacement *axes,
bool touching)
{
struct input_event *ev;
double t, l, r = 0, b = 0; /* top, left, right, bottom */
bool filter_abs_xy = false;
if (d->interface->touch_move) {
d->interface->touch_move(d, slot, x, y);
if (!d->semi_mt.is_semi_mt) {
slot_move(d, slot, x, y, axes, touching, filter_abs_xy);
return;
}
ev = d->interface->touch_move_events;
while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
int value = litest_auto_assign_value(d,
ev,
slot,
x,
y,
axes,
touching);
if (value != LITEST_AUTO_ASSIGN)
litest_event(d, ev->type, ev->code, value);
ev++;
if (d->ntouches_down > 2 || slot > 1)
return;
if (d->ntouches_down == 1) {
l = x;
t = y;
} else {
int other = (slot + 1) % 2;
l = min(x, d->semi_mt.touches[other].x);
t = min(y, d->semi_mt.touches[other].y);
r = max(x, d->semi_mt.touches[other].x);
b = max(y, d->semi_mt.touches[other].y);
}
litest_push_event_frame(d);
slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
if (d->ntouches_down == 2) {
filter_abs_xy = true;
slot_move(d, 1, r, b, axes, touching, filter_abs_xy);
}
litest_pop_event_frame(d);
d->semi_mt.touches[slot].x = x;
d->semi_mt.touches[slot].y = y;
}
void
litest_touch_up(struct litest_device *d, unsigned int slot)
{
if (!d->semi_mt.is_semi_mt) {
touch_up(d, slot);
return;
}
if (d->ntouches_down > 2 || slot > 1)
return;
litest_push_event_frame(d);
touch_up(d, d->ntouches_down - 1);
/* if we have one finger left, send x/y coords for that finger left.
this is likely to happen with a real touchpad */
if (d->ntouches_down == 1) {
bool touching = true;
bool filter_abs_xy = false;
int other = (slot + 1) % 2;
slot_move(d,
0,
d->semi_mt.touches[other].x,
d->semi_mt.touches[other].y,
NULL,
touching,
filter_abs_xy);
}
litest_pop_event_frame(d);
}
void
@ -1650,6 +1806,28 @@ litest_touch_move_to(struct litest_device *d,
litest_touch_move(d, slot, x_to, y_to);
}
void
litest_touch_move_to_extended(struct litest_device *d,
unsigned int slot,
double x_from, double y_from,
double x_to, double y_to,
struct axis_replacement *axes,
int steps, int sleep_ms)
{
for (int i = 1; i < steps - 1; i++) {
litest_touch_move_extended(d, slot,
x_from + (x_to - x_from)/steps * i,
y_from + (y_to - y_from)/steps * i,
axes);
if (sleep_ms) {
libinput_dispatch(d->libinput);
msleep(sleep_ms);
libinput_dispatch(d->libinput);
}
}
litest_touch_move_extended(d, slot, x_to, y_to, axes);
}
static int
auto_assign_tablet_value(struct litest_device *d,
const struct input_event *ev,
@ -1784,7 +1962,13 @@ litest_hover_start(struct litest_device *d,
double x,
double y)
{
litest_slot_start(d, slot, x, y, NULL, 0);
struct axis_replacement axes[] = {
{ABS_MT_PRESSURE, 0 },
{ABS_PRESSURE, 0 },
{-1, -1 },
};
litest_slot_start(d, slot, x, y, axes, 0);
}
void
@ -1823,7 +2007,13 @@ void
litest_hover_move(struct litest_device *d, unsigned int slot,
double x, double y)
{
litest_slot_move(d, slot, x, y, NULL, false);
struct axis_replacement axes[] = {
{ABS_MT_PRESSURE, 0 },
{ABS_PRESSURE, 0 },
{-1, -1 },
};
litest_slot_move(d, slot, x, y, axes, false);
}
void
@ -3045,16 +3235,17 @@ litest_timeout_trackpoint(void)
void
litest_push_event_frame(struct litest_device *dev)
{
litest_assert(!dev->skip_ev_syn);
dev->skip_ev_syn = true;
litest_assert(dev->skip_ev_syn >= 0);
dev->skip_ev_syn++;
}
void
litest_pop_event_frame(struct litest_device *dev)
{
litest_assert(dev->skip_ev_syn);
dev->skip_ev_syn = false;
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_assert(dev->skip_ev_syn > 0);
dev->skip_ev_syn--;
if (dev->skip_ev_syn == 0)
litest_event(dev, EV_SYN, SYN_REPORT, 0);
}
static void

View file

@ -265,6 +265,23 @@ enum litest_device_feature {
LITEST_SWITCH = 1 << 26,
};
/* this is a semi-mt device, so we keep track of the touches that the tests
* send and modify them so that the first touch is always slot 0 and sends
* the top-left of the bounding box, the second is always slot 1 and sends
* the bottom-right of the bounding box.
* Lifting any of two fingers terminates slot 1
*/
struct litest_semi_mt {
bool is_semi_mt;
int tracking_id;
/* The actual touches requested by the test for the two slots
* in the 0..100 range used by litest */
struct {
double x, y;
} touches[2];
};
struct litest_device {
struct libevdev *evdev;
struct libevdev_uinput *uinput;
@ -274,7 +291,8 @@ struct litest_device {
struct litest_device_interface *interface;
int ntouches_down;
bool skip_ev_syn;
int skip_ev_syn;
struct litest_semi_mt semi_mt; /** only used for semi-mt device */
void *private; /* device-specific data */
};
@ -441,6 +459,14 @@ litest_touch_move_to(struct litest_device *d,
double x_to, double y_to,
int steps, int sleep_ms);
void
litest_touch_move_to_extended(struct litest_device *d,
unsigned int slot,
double x_from, double y_from,
double x_to, double y_to,
struct axis_replacement *axes,
int steps, int sleep_ms);
void
litest_touch_move_two_touches(struct litest_device *d,
double x0, double y0,
@ -681,21 +707,6 @@ litest_push_event_frame(struct litest_device *dev);
void
litest_pop_event_frame(struct litest_device *dev);
/* this is a semi-mt device, so we keep track of the touches that the tests
* send and modify them so that the first touch is always slot 0 and sends
* the top-left of the bounding box, the second is always slot 1 and sends
* the bottom-right of the bounding box.
* Lifting any of two fingers terminates slot 1
*/
struct litest_semi_mt {
int tracking_id;
/* The actual touches requested by the test for the two slots
* in the 0..100 range used by litest */
struct {
double x, y;
} touches[2];
};
void
litest_semi_mt_touch_down(struct litest_device *d,
struct litest_semi_mt *semi_mt,

View file

@ -1686,6 +1686,7 @@ START_TEST(touchpad_semi_mt_hover_down)
litest_event(dev, EV_ABS, ABS_X, x + 100);
litest_event(dev, EV_ABS, ABS_Y, y + 100);
litest_event(dev, EV_ABS, ABS_PRESSURE, 50);
litest_event(dev, EV_KEY, BTN_TOUCH, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -1712,6 +1713,7 @@ START_TEST(touchpad_semi_mt_hover_down)
/* go back to hover */
hover_continue(dev, 0, x, y);
litest_event(dev, EV_ABS, ABS_PRESSURE, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -1752,6 +1754,7 @@ START_TEST(touchpad_semi_mt_hover_down_hover_down)
/* touch */
litest_event(dev, EV_ABS, ABS_X, x + 100);
litest_event(dev, EV_ABS, ABS_Y, y + 100);
litest_event(dev, EV_ABS, ABS_PRESSURE, 50);
litest_event(dev, EV_KEY, BTN_TOUCH, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -1773,6 +1776,7 @@ START_TEST(touchpad_semi_mt_hover_down_hover_down)
/* go back to hover */
hover_continue(dev, 0, x, y);
litest_event(dev, EV_ABS, ABS_PRESSURE, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -1796,7 +1800,10 @@ START_TEST(touchpad_semi_mt_hover_down_hover_down)
litest_assert_empty_queue(li);
/* start a new touch to be sure */
litest_push_event_frame(dev);
litest_touch_down(dev, 0, 50, 50);
litest_event(dev, EV_ABS, ABS_PRESSURE, 50);
litest_pop_event_frame(dev);
litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10);
litest_touch_up(dev, 0);
@ -1845,6 +1852,7 @@ START_TEST(touchpad_semi_mt_hover_down_up)
litest_assert_empty_queue(li);
litest_event(dev, EV_ABS, ABS_PRESSURE, 50);
litest_event(dev, EV_KEY, BTN_TOUCH, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -1930,6 +1938,7 @@ START_TEST(touchpad_semi_mt_hover_2fg_1fg_down)
/* two slots active, but BTN_TOOL_FINGER only */
hover_start(dev, 0, x, y);
hover_start(dev, 1, x + 500, y + 500);
litest_event(dev, EV_ABS, ABS_PRESSURE, 50);
litest_event(dev, EV_KEY, BTN_TOUCH, 1);
litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -1948,6 +1957,7 @@ START_TEST(touchpad_semi_mt_hover_2fg_1fg_down)
litest_event(dev, EV_SYN, SYN_REPORT, 0);
}
litest_event(dev, EV_ABS, ABS_PRESSURE, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
@ -1969,6 +1979,7 @@ START_TEST(touchpad_semi_mt_hover_2fg_up)
litest_push_event_frame(dev);
litest_touch_move(dev, 0, 72, 50);
litest_touch_move(dev, 1, 52, 50);
litest_event(dev, EV_ABS, ABS_PRESSURE, 0);
litest_event(dev, EV_KEY, BTN_TOUCH, 0);
litest_pop_event_frame(dev);
@ -2152,6 +2163,24 @@ START_TEST(touchpad_hover_2fg_1fg_down)
}
END_TEST
START_TEST(touchpad_hover_1fg_tap)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_enable_tap(dev->libinput_device);
litest_drain_events(li);
litest_hover_start(dev, 0, 50, 50);
litest_hover_end(dev, 0);
libinput_dispatch(li);
litest_assert_empty_queue(li);
}
END_TEST
static void
assert_btnevent_from_device(struct litest_device *device,
unsigned int button,
@ -4669,6 +4698,222 @@ START_TEST(touchpad_disabled_double_mouse_one_suspended)
}
END_TEST
static inline bool
touchpad_has_pressure(struct litest_device *dev)
{
struct libevdev *evdev = dev->evdev;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE))
return true;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_PRESSURE) &&
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT))
return true;
return false;
}
START_TEST(touchpad_pressure)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 1 },
{ ABS_PRESSURE, 1 },
{ -1, 0 }
};
double pressure; /* in percent */
double threshold = 12.0;
if (!touchpad_has_pressure(dev))
return;
litest_drain_events(li);
for (pressure = 1; pressure <= threshold + 1; pressure++) {
litest_axis_set_value(axes, ABS_MT_PRESSURE, pressure);
litest_axis_set_value(axes, ABS_PRESSURE, pressure);
litest_touch_down_extended(dev, 0, 50, 50, axes);
litest_touch_move_to_extended(dev, 0,
50, 50,
80, 80,
axes, 10, 1);
litest_touch_up(dev, 0);
if (pressure < threshold)
litest_assert_empty_queue(li);
else
litest_assert_only_typed_events(li,
LIBINPUT_EVENT_POINTER_MOTION);
}
}
END_TEST
START_TEST(touchpad_pressure_2fg)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 5 },
{ ABS_PRESSURE, 5 },
{ -1, 0 }
};
if (!touchpad_has_pressure(dev))
return;
litest_drain_events(li);
litest_touch_down(dev, 0, 30, 50);
litest_touch_down_extended(dev, 1, 50, 50, axes);
libinput_dispatch(li);
litest_touch_move_to(dev, 0, 50, 50, 80, 80, 10, 1);
libinput_dispatch(li);
litest_assert_only_typed_events(li,
LIBINPUT_EVENT_POINTER_MOTION);
litest_touch_move_to_extended(dev, 1,
50, 50,
80, 80,
axes, 10, 1);
litest_assert_empty_queue(li);
litest_touch_move_to(dev, 0, 80, 80, 20, 50, 10, 1);
litest_touch_move_to_extended(dev, 1,
80, 80,
50, 50,
axes, 10, 1);
litest_assert_only_typed_events(li,
LIBINPUT_EVENT_POINTER_MOTION);
}
END_TEST
START_TEST(touchpad_pressure_2fg_st)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 5 },
{ ABS_PRESSURE, 5 },
{ -1, 0 }
};
if (!touchpad_has_pressure(dev))
return;
/* This is a bit of a weird test. We expect two fingers to be down as
* soon as doubletap is set, regardless of pressure. But we don't
* have 2fg scrolling on st devices and 2 fingers down on a touchpad
* without 2fg scrolling simply does not generate events. But that's
* the same result as if the fingers were ignored because of
* pressure and we cannot know the difference.
* So this test only keeps your CPU warm, not much else.
*/
litest_drain_events(li);
litest_touch_down_extended(dev, 0, 50, 50, axes);
libinput_dispatch(li);
litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 0);
litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
litest_touch_move_to_extended(dev, 0,
50, 50,
80, 80,
axes, 10, 1);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_pressure_tap)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 5 },
{ ABS_PRESSURE, 5 },
{ -1, 0 }
};
if (!touchpad_has_pressure(dev))
return;
litest_enable_tap(dev->libinput_device);
litest_drain_events(li);
litest_touch_down_extended(dev, 0, 50, 50, axes);
libinput_dispatch(li);
litest_touch_up(dev, 0);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_pressure_tap_2fg)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 5 },
{ ABS_PRESSURE, 5 },
{ -1, 0 }
};
if (!touchpad_has_pressure(dev))
return;
litest_enable_tap(dev->libinput_device);
litest_drain_events(li);
/* tap but too light */
litest_touch_down_extended(dev, 0, 40, 50, axes);
litest_touch_down_extended(dev, 1, 50, 50, axes);
libinput_dispatch(li);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
litest_assert_empty_queue(li);
}
END_TEST
START_TEST(touchpad_pressure_tap_2fg_1fg_light)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
struct axis_replacement axes[] = {
{ ABS_MT_PRESSURE, 5 },
{ ABS_PRESSURE, 5 },
{ -1, 0 }
};
if (!touchpad_has_pressure(dev))
return;
litest_enable_tap(dev->libinput_device);
litest_drain_events(li);
/* double-tap with one finger too light */
litest_touch_down(dev, 0, 40, 50);
litest_touch_down_extended(dev, 1, 50, 50, axes);
libinput_dispatch(li);
litest_touch_up(dev, 0);
litest_touch_up(dev, 1);
libinput_dispatch(li);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_PRESSED);
libinput_event_destroy(event);
litest_timeout_tap();
libinput_dispatch(li);
event = libinput_get_event(li);
litest_is_button_event(event,
BTN_LEFT,
LIBINPUT_BUTTON_STATE_RELEASED);
libinput_event_destroy(event);
}
END_TEST
void
litest_setup_tests_touchpad(void)
{
@ -4740,6 +4985,7 @@ litest_setup_tests_touchpad(void)
litest_add("touchpad:hover", touchpad_hover_down_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
litest_add("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
litest_add("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
litest_add("touchpad:hover", touchpad_hover_1fg_tap, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY);
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_mb_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS);
@ -4808,4 +5054,11 @@ litest_setup_tests_touchpad(void)
litest_add_for_device("touchpad:sendevents", touchpad_disabled_on_mouse_suspend_mouse, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add_for_device("touchpad:sendevents", touchpad_disabled_double_mouse, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add_for_device("touchpad:sendevents", touchpad_disabled_double_mouse_one_suspended, LITEST_SYNAPTICS_CLICKPAD_X220);
litest_add("touchpad:pressure", touchpad_pressure, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:pressure", touchpad_pressure_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:pressure", touchpad_pressure_2fg_st, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
litest_add("touchpad:pressure", touchpad_pressure_tap, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:pressure", touchpad_pressure_tap_2fg, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("touchpad:pressure", touchpad_pressure_tap_2fg_1fg_light, LITEST_TOUCHPAD, LITEST_ANY);
}