From 61721e446a28f741b6143e0fbde1fd2ae48263e0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2015 13:14:17 +0100 Subject: [PATCH 001/165] Fix scroll-threshold check for edge-scrolling to use normalized coordinates The DEFAULT_SCROLL_THRESHOLD value is a TP_MM_TO_DPI_NORMALIZED value and as such should be compared to a normalized value. But since commit 8101e43774 ("touchpad: switch delta handling to typesafe coordinates"), the initial_dx / initial_dy values initial_delta points to are no longer in normalized coordinates, as the result of tp_normalize_delta is now stored into the normalized value. This commit changes the check to use the delta pointer which does point to the normalized x/y values. While at it also use the zero variable to set normalized to zero rather then manually setting x and y to 0. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-edge-scroll.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 26d6bbcf..d304316a 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -313,7 +313,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) struct tp_touch *t; enum libinput_pointer_axis axis; double *delta; - double initial_dx, initial_dy, *initial_delta; + double initial_dx, initial_dy; struct normalized_coords normalized; const struct normalized_coords zero = { 0.0, 0.0 }; const struct discrete_coords zero_discrete = { 0.0, 0.0 }; @@ -340,12 +340,10 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) case EDGE_RIGHT: axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL; delta = &normalized.y; - initial_delta = &initial_dy; break; case EDGE_BOTTOM: axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL; delta = &normalized.x; - initial_delta = &initial_dx; break; default: /* EDGE_RIGHT | EDGE_BOTTOM */ continue; /* Don't know direction yet, skip */ @@ -369,10 +367,8 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) initial_dx, initial_dy, &normalized); - if (fabs(*initial_delta) < DEFAULT_SCROLL_THRESHOLD) { - normalized.x = 0.0; - normalized.y = 0.0; - } + if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD) + normalized = zero; break; case EDGE_SCROLL_TOUCH_STATE_EDGE: break; From fe93145f8653a3f3136445768a1e84bcce903d84 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2015 13:14:18 +0100 Subject: [PATCH 002/165] Add a delta_coords type and use it were applicable tp_normalize_coords is one of the last functions taking separate x, y values rather a coordinate pair, this commit cleans this up. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-edge-scroll.c | 10 +++------- src/evdev-mt-touchpad-tap.c | 7 +++---- src/evdev-mt-touchpad.c | 25 ++++++++++++------------- src/evdev-mt-touchpad.h | 14 ++++++++------ src/libinput-private.h | 20 ++++++++++++++++++++ 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index d304316a..91ebd28c 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -313,7 +313,6 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) struct tp_touch *t; enum libinput_pointer_axis axis; double *delta; - double initial_dx, initial_dy; struct normalized_coords normalized; const struct normalized_coords zero = { 0.0, 0.0 }; const struct discrete_coords zero_discrete = { 0.0, 0.0 }; @@ -361,12 +360,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) t->scroll.edge_state); break; case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW: - initial_dx = t->point.x - t->scroll.initial.x; - initial_dy = t->point.y - t->scroll.initial.y; - tp_normalize_delta(tp, - initial_dx, - initial_dy, - &normalized); + normalized = tp_normalize_delta(tp, + device_delta(t->point, + t->scroll.initial)); if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD) normalized = zero; break; diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 6bd7c582..09713922 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -531,12 +531,11 @@ tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp, struct tp_touch *t) { int threshold = DEFAULT_TAP_MOVE_THRESHOLD; - double dx, dy; struct normalized_coords normalized; - dx = abs(t->tap.initial.x - t->point.x); - dy = abs(t->tap.initial.y - t->point.y); - tp_normalize_delta(tp, dx, dy, &normalized); + normalized = tp_normalize_delta(tp, + device_delta(t->point, + t->tap.initial)); return normalized.x * normalized.x + normalized.y * normalized.y > threshold * threshold; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 856af4a2..37ad13a6 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -261,23 +261,22 @@ tp_estimate_delta(int x0, int x1, int x2, int x3) struct normalized_coords tp_get_delta(struct tp_touch *t) { - double dx, dy; /* in device coords */ - struct normalized_coords normalized = { 0.0, 0.0 }; + struct delta_coords delta; + const struct normalized_coords zero = { 0.0, 0.0 }; if (t->history.count < TOUCHPAD_MIN_SAMPLES) - return normalized; + return zero; - dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x, - tp_motion_history_offset(t, 1)->x, - tp_motion_history_offset(t, 2)->x, - tp_motion_history_offset(t, 3)->x); - dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y, - tp_motion_history_offset(t, 1)->y, - tp_motion_history_offset(t, 2)->y, - tp_motion_history_offset(t, 3)->y); - tp_normalize_delta(t->tp, dx, dy, &normalized); + delta.dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x, + tp_motion_history_offset(t, 1)->x, + tp_motion_history_offset(t, 2)->x, + tp_motion_history_offset(t, 3)->x); + delta.dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y, + tp_motion_history_offset(t, 1)->y, + tp_motion_history_offset(t, 2)->y, + tp_motion_history_offset(t, 3)->y); - return normalized; + return tp_normalize_delta(t->tp, delta); } static void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 9980f900..0c6d87ee 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -274,13 +274,15 @@ struct tp_dispatch { #define tp_for_each_touch(_tp, _t) \ for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) -static inline void -tp_normalize_delta(struct tp_dispatch *tp, - double dx, double dy, - struct normalized_coords *normalized) +static inline struct normalized_coords +tp_normalize_delta(struct tp_dispatch *tp, struct delta_coords delta) { - normalized->x = dx * tp->accel.x_scale_coeff; - normalized->y = dy * tp->accel.y_scale_coeff; + struct normalized_coords normalized; + + normalized.x = delta.dx * tp->accel.x_scale_coeff; + normalized.y = delta.dy * tp->accel.y_scale_coeff; + + return normalized; } struct normalized_coords diff --git a/src/libinput-private.h b/src/libinput-private.h index e39ac5ea..73f8d157 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -37,6 +37,14 @@ struct device_coords { int x, y; }; +/* + * A delta between 2 device coordinates, + * may be non-discrete because of averaging. + */ +struct delta_coords { + double dx, dy; +}; + /* A dpi-normalized coordinate pair */ struct normalized_coords { double x, y; @@ -353,4 +361,16 @@ libinput_now(struct libinput *libinput) return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; } + +static inline struct delta_coords +device_delta(struct device_coords a, struct device_coords b) +{ + struct delta_coords delta; + + delta.dx = a.x - b.x; + delta.dy = a.y - b.y; + + return delta; +} + #endif /* LIBINPUT_PRIVATE_H */ From 674490ca60e085593ebaf8d17e4486d2a2036ced Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2015 13:14:19 +0100 Subject: [PATCH 003/165] Add a normalized_length helper function and use this where applicable Add a normalized_length helper function and use this where applicable, just a minor cleanup. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 12 ++++-------- src/filter.c | 4 +--- src/libinput-private.h | 7 +++++++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 09713922..4ba4ad28 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -530,15 +530,11 @@ static bool tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp, struct tp_touch *t) { - int threshold = DEFAULT_TAP_MOVE_THRESHOLD; - struct normalized_coords normalized; + struct normalized_coords norm = + tp_normalize_delta(tp, device_delta(t->point, + t->tap.initial)); - normalized = tp_normalize_delta(tp, - device_delta(t->point, - t->tap.initial)); - - return normalized.x * normalized.x + normalized.y * normalized.y - > threshold * threshold; + return normalized_length(norm) > DEFAULT_TAP_MOVE_THRESHOLD; } static bool diff --git a/src/filter.c b/src/filter.c index dc299286..dd4bd58d 100644 --- a/src/filter.c +++ b/src/filter.c @@ -137,11 +137,9 @@ tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset) static double calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time) { - double distance; double tdelta = time - tracker->time + 1; - distance = hypot(tracker->delta.x, tracker->delta.y); - return distance / tdelta; /* units/ms */ + return normalized_length(tracker->delta) / tdelta; /* units/ms */ } static double diff --git a/src/libinput-private.h b/src/libinput-private.h index 73f8d157..31f2dbc1 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -24,6 +24,7 @@ #define LIBINPUT_PRIVATE_H #include +#include #include "linux/input.h" @@ -373,4 +374,10 @@ device_delta(struct device_coords a, struct device_coords b) return delta; } +static inline double +normalized_length(struct normalized_coords norm) +{ + return hypot(norm.x, norm.y); +} + #endif /* LIBINPUT_PRIVATE_H */ From b87251ca714bab509958b712a6d3486925666bfd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2015 16:33:29 +0100 Subject: [PATCH 004/165] Add a normalized_is_zero helper function Add a normalized_is_zero helper function, and use it where applicable. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 5 ++--- src/evdev-mt-touchpad.c | 2 +- src/evdev.c | 6 ++---- src/libinput-private.h | 6 ++++++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index f852ff5a..aba58732 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -99,8 +99,7 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time) tp_filter_motion(tp, &delta.x, &delta.y, &unaccel.x, &unaccel.y, time); - if (delta.x != 0.0 || delta.y != 0.0 || - unaccel.x != 0.0 || unaccel.y != 0.0) { + if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) { pointer_notify_motion(&tp->device->base, time, &delta, &unaccel); } @@ -114,7 +113,7 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) delta = tp_get_average_touches_delta(tp); tp_filter_motion(tp, &delta.x, &delta.y, NULL, NULL, time); - if (delta.x == 0.0 && delta.y == 0.0) + if (normalized_is_zero(delta)) return; tp_gesture_start(tp, time); diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 37ad13a6..2078bd7d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -70,7 +70,7 @@ tp_filter_motion(struct tp_dispatch *tp, unaccelerated.x = *dx; unaccelerated.y = *dy; - if (unaccelerated.x != 0.0 || unaccelerated.y != 0.0) + if (!normalized_is_zero(unaccelerated)) accelerated = filter_dispatch(tp->device->pointer.filter, &unaccelerated, tp, diff --git a/src/evdev.c b/src/evdev.c index 52b89d38..a972b9d0 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -268,10 +268,8 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) /* Apply pointer acceleration. */ accel = filter_dispatch(device->pointer.filter, &unaccel, device, time); - if (accel.x == 0.0 && accel.y == 0.0 && - unaccel.x == 0.0 && unaccel.y == 0.0) { + if (normalized_is_zero(accel) && normalized_is_zero(unaccel)) break; - } pointer_notify_motion(base, time, &accel, &unaccel); break; @@ -2098,7 +2096,7 @@ evdev_post_scroll(struct evdev_device *device, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) event.x = 0.0; - if (event.x != 0.0 || event.y != 0.0) { + if (!normalized_is_zero(event)) { const struct discrete_coords zero_discrete = { 0.0, 0.0 }; evdev_notify_axis(device, time, diff --git a/src/libinput-private.h b/src/libinput-private.h index 31f2dbc1..722505c3 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -380,4 +380,10 @@ normalized_length(struct normalized_coords norm) return hypot(norm.x, norm.y); } +static inline int +normalized_is_zero(struct normalized_coords norm) +{ + return norm.x == 0.0 && norm.y == 0.0; +} + #endif /* LIBINPUT_PRIVATE_H */ From 9f353b6678c82d44984fb78e58ee0d0f542c4862 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2015 16:43:24 +0100 Subject: [PATCH 005/165] Change tp_filter_motion to normalized_coords Change tp_filter_motion to use normalized_coords, rather then having it take separate x and y values. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad-edge-scroll.c | 3 +-- src/evdev-mt-touchpad-gestures.c | 8 ++++---- src/evdev-mt-touchpad.c | 29 ++++++----------------------- src/evdev-mt-touchpad.h | 5 ++--- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 91ebd28c..0cdbb19d 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -349,8 +349,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time) } normalized = tp_get_delta(t); - tp_filter_motion(tp, &normalized.x, &normalized.y, - NULL, NULL, time); + normalized = tp_filter_motion(tp, &normalized, time); switch (t->scroll.edge_state) { case EDGE_SCROLL_TOUCH_STATE_NONE: diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index aba58732..7dbf4c4a 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -93,11 +93,11 @@ tp_gesture_post_pointer_motion(struct tp_dispatch *tp, uint64_t time) /* When a clickpad is clicked, combine motion of all active touches */ if (tp->buttons.is_clickpad && tp->buttons.state) - delta = tp_get_combined_touches_delta(tp); + unaccel = tp_get_combined_touches_delta(tp); else - delta = tp_get_average_touches_delta(tp); + unaccel = tp_get_average_touches_delta(tp); - tp_filter_motion(tp, &delta.x, &delta.y, &unaccel.x, &unaccel.y, time); + delta = tp_filter_motion(tp, &unaccel, time); if (!normalized_is_zero(delta) || !normalized_is_zero(unaccel)) { pointer_notify_motion(&tp->device->base, time, @@ -111,7 +111,7 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) struct normalized_coords delta; delta = tp_get_average_touches_delta(tp); - tp_filter_motion(tp, &delta.x, &delta.y, NULL, NULL, time); + delta = tp_filter_motion(tp, &delta, time); if (normalized_is_zero(delta)) return; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 2078bd7d..afc91785 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -58,33 +58,16 @@ tp_motion_history_offset(struct tp_touch *t, int offset) return &t->history.samples[offset_index]; } -void +struct normalized_coords tp_filter_motion(struct tp_dispatch *tp, - double *dx, double *dy, - double *dx_unaccel, double *dy_unaccel, + const struct normalized_coords *unaccelerated, uint64_t time) { - struct normalized_coords unaccelerated; - struct normalized_coords accelerated; + if (normalized_is_zero(*unaccelerated)) + return *unaccelerated; - unaccelerated.x = *dx; - unaccelerated.y = *dy; - - if (!normalized_is_zero(unaccelerated)) - accelerated = filter_dispatch(tp->device->pointer.filter, - &unaccelerated, - tp, - time); - else - accelerated = unaccelerated; - - if (dx_unaccel) - *dx_unaccel = unaccelerated.x; - if (dy_unaccel) - *dy_unaccel = unaccelerated.y; - - *dx = accelerated.x; - *dy = accelerated.y; + return filter_dispatch(tp->device->pointer.filter, + unaccelerated, tp, time); } static inline void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 0c6d87ee..5d52723c 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -288,10 +288,9 @@ tp_normalize_delta(struct tp_dispatch *tp, struct delta_coords delta) struct normalized_coords tp_get_delta(struct tp_touch *t); -void +struct normalized_coords tp_filter_motion(struct tp_dispatch *tp, - double *dx, double *dy, - double *dx_unaccel, double *dy_unaccel, + const struct normalized_coords *unaccelerated, uint64_t time); int From 209215946b90c76d6ae8cb87bd56a1210d3e5575 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Mar 2015 11:01:04 +0100 Subject: [PATCH 006/165] Rename delta_coords to device_float_coords What we really need is not a specific delta type, but a type which can hold non discrete device coordinates, this is e.g. also needed for the center coordinates of gestures. So rename delta_coords to device_float_coords to properly reflect what we really need. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 18 +++++++++--------- src/evdev-mt-touchpad.h | 6 +++--- src/libinput-private.h | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index afc91785..b5986952 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -244,20 +244,20 @@ tp_estimate_delta(int x0, int x1, int x2, int x3) struct normalized_coords tp_get_delta(struct tp_touch *t) { - struct delta_coords delta; + struct device_float_coords delta; const struct normalized_coords zero = { 0.0, 0.0 }; if (t->history.count < TOUCHPAD_MIN_SAMPLES) return zero; - delta.dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x, - tp_motion_history_offset(t, 1)->x, - tp_motion_history_offset(t, 2)->x, - tp_motion_history_offset(t, 3)->x); - delta.dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y, - tp_motion_history_offset(t, 1)->y, - tp_motion_history_offset(t, 2)->y, - tp_motion_history_offset(t, 3)->y); + delta.x = tp_estimate_delta(tp_motion_history_offset(t, 0)->x, + tp_motion_history_offset(t, 1)->x, + tp_motion_history_offset(t, 2)->x, + tp_motion_history_offset(t, 3)->x); + delta.y = tp_estimate_delta(tp_motion_history_offset(t, 0)->y, + tp_motion_history_offset(t, 1)->y, + tp_motion_history_offset(t, 2)->y, + tp_motion_history_offset(t, 3)->y); return tp_normalize_delta(t->tp, delta); } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 5d52723c..19a262e3 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -275,12 +275,12 @@ struct tp_dispatch { for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) static inline struct normalized_coords -tp_normalize_delta(struct tp_dispatch *tp, struct delta_coords delta) +tp_normalize_delta(struct tp_dispatch *tp, struct device_float_coords delta) { struct normalized_coords normalized; - normalized.x = delta.dx * tp->accel.x_scale_coeff; - normalized.y = delta.dy * tp->accel.y_scale_coeff; + normalized.x = delta.x * tp->accel.x_scale_coeff; + normalized.y = delta.y * tp->accel.y_scale_coeff; return normalized; } diff --git a/src/libinput-private.h b/src/libinput-private.h index 722505c3..1da7db9d 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -39,11 +39,11 @@ struct device_coords { }; /* - * A delta between 2 device coordinates, - * may be non-discrete because of averaging. + * A coordinate pair in device coordinates, capable of holding non discrete + * values, this is necessary e.g. when device coordinates get averaged. */ -struct delta_coords { - double dx, dy; +struct device_float_coords { + double x, y; }; /* A dpi-normalized coordinate pair */ @@ -363,13 +363,13 @@ libinput_now(struct libinput *libinput) return ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000; } -static inline struct delta_coords +static inline struct device_float_coords device_delta(struct device_coords a, struct device_coords b) { - struct delta_coords delta; + struct device_float_coords delta; - delta.dx = a.x - b.x; - delta.dy = a.y - b.y; + delta.x = a.x - b.x; + delta.y = a.y - b.y; return delta; } From 254f4f255f1c825bcfc192b2e68d288baf49a40f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Mar 2015 15:05:19 +0100 Subject: [PATCH 007/165] Change vector_get_direction input to a normalized_coords struct Change vector_get_direction input to a normalized_coords type rather than passing in a separate x,y pair, and rename it normalized_get_direction to match. Since it now depends on the normalized_coords type which gets declared in libinput-private.h also move it to libinput-private.h . Note this commit also contains a functional change wrt the get_direction usuage in the palm detection. The palm-detection code was calling get_direction on non normalized coordinates, this commits changes the code to normalize the coordinates first. This is the right thing to do as calling get_direction on non normalized coordinates may result in a wrong direction getting returned when the x and y resolution of the touchpad are not identical. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 7 +++-- src/filter.c | 2 +- src/libinput-private.h | 57 +++++++++++++++++++++++++++++++++++++++++ src/libinput-util.h | 57 ----------------------------------------- 4 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b5986952..7d6d838e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -448,6 +448,8 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { const int PALM_TIMEOUT = 200; /* ms */ const int DIRECTIONS = NE|E|SE|SW|W|NW; + struct device_float_coords delta; + int dirs; /* If labelled a touch as palm, we unlabel as palm when we move out of the palm edge zone within the timeout, provided @@ -456,8 +458,9 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) if (t->palm.is_palm) { if (time < t->palm.time + PALM_TIMEOUT && (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) { - int dirs = vector_get_direction(t->point.x - t->palm.first.x, - t->point.y - t->palm.first.y); + delta = device_delta(t->point, t->palm.first); + dirs = normalized_get_direction( + tp_normalize_delta(tp, delta)); if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) { t->palm.is_palm = false; } diff --git a/src/filter.c b/src/filter.c index dd4bd58d..033201fc 100644 --- a/src/filter.c +++ b/src/filter.c @@ -122,7 +122,7 @@ feed_trackers(struct pointer_accelerator *accel, trackers[current].delta.x = 0.0; trackers[current].delta.y = 0.0; trackers[current].time = time; - trackers[current].dir = vector_get_direction(delta->x, delta->y); + trackers[current].dir = normalized_get_direction(*delta); } static struct pointer_tracker * diff --git a/src/libinput-private.h b/src/libinput-private.h index 1da7db9d..36b3b40f 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -386,4 +386,61 @@ normalized_is_zero(struct normalized_coords norm) return norm.x == 0.0 && norm.y == 0.0; } +enum directions { + N = 1 << 0, + NE = 1 << 1, + E = 1 << 2, + SE = 1 << 3, + S = 1 << 4, + SW = 1 << 5, + W = 1 << 6, + NW = 1 << 7, + UNDEFINED_DIRECTION = 0xff +}; + +static inline int +normalized_get_direction(struct normalized_coords norm) +{ + int dir = UNDEFINED_DIRECTION; + int d1, d2; + double r; + + if (fabs(norm.x) < 2.0 && fabs(norm.y) < 2.0) { + if (norm.x > 0.0 && norm.y > 0.0) + dir = S | SE | E; + else if (norm.x > 0.0 && norm.y < 0.0) + dir = N | NE | E; + else if (norm.x < 0.0 && norm.y > 0.0) + dir = S | SW | W; + else if (norm.x < 0.0 && norm.y < 0.0) + dir = N | NW | W; + else if (norm.x > 0.0) + dir = NE | E | SE; + else if (norm.x < 0.0) + dir = NW | W | SW; + else if (norm.y > 0.0) + dir = SE | S | SW; + else if (norm.y < 0.0) + dir = NE | N | NW; + } else { + /* Calculate r within the interval [0 to 8) + * + * r = [0 .. 2π] where 0 is North + * d_f = r / 2π ([0 .. 1)) + * d_8 = 8 * d_f + */ + r = atan2(norm.y, norm.x); + r = fmod(r + 2.5*M_PI, 2*M_PI); + r *= 4*M_1_PI; + + /* Mark one or two close enough octants */ + d1 = (int)(r + 0.9) % 8; + d2 = (int)(r + 0.1) % 8; + + dir = (1 << d1) | (1 << d2); + } + + return dir; +} + #endif /* LIBINPUT_PRIVATE_H */ diff --git a/src/libinput-util.h b/src/libinput-util.h index 04515d6d..30f59ecf 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -98,63 +98,6 @@ msleep(unsigned int ms) usleep(ms * 1000); } -enum directions { - N = 1 << 0, - NE = 1 << 1, - E = 1 << 2, - SE = 1 << 3, - S = 1 << 4, - SW = 1 << 5, - W = 1 << 6, - NW = 1 << 7, - UNDEFINED_DIRECTION = 0xff -}; - -static inline int -vector_get_direction(double dx, double dy) -{ - int dir = UNDEFINED_DIRECTION; - int d1, d2; - double r; - - if (fabs(dx) < 2.0 && fabs(dy) < 2.0) { - if (dx > 0.0 && dy > 0.0) - dir = S | SE | E; - else if (dx > 0.0 && dy < 0.0) - dir = N | NE | E; - else if (dx < 0.0 && dy > 0.0) - dir = S | SW | W; - else if (dx < 0.0 && dy < 0.0) - dir = N | NW | W; - else if (dx > 0.0) - dir = NE | E | SE; - else if (dx < 0.0) - dir = NW | W | SW; - else if (dy > 0.0) - dir = SE | S | SW; - else if (dy < 0.0) - dir = NE | N | NW; - } else { - /* Calculate r within the interval [0 to 8) - * - * r = [0 .. 2π] where 0 is North - * d_f = r / 2π ([0 .. 1)) - * d_8 = 8 * d_f - */ - r = atan2(dy, dx); - r = fmod(r + 2.5*M_PI, 2*M_PI); - r *= 4*M_1_PI; - - /* Mark one or two close enough octants */ - d1 = (int)(r + 0.9) % 8; - d2 = (int)(r + 0.1) % 8; - - dir = (1 << d1) | (1 << d2); - } - - return dir; -} - static inline int long_bit_is_set(const unsigned long *array, int bit) { From fe1a81c06514b2958cdf2a0014aa409272eb9714 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 08:09:41 +1000 Subject: [PATCH 008/165] include: update to v3.18 header Signed-off-by: Peter Hutterer --- include/linux/input.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/include/linux/input.h b/include/linux/input.h index 2ac46630..e5acbe5b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -459,7 +459,10 @@ struct input_keymap_entry { #define KEY_VIDEO_NEXT 241 /* drive next video source */ #define KEY_VIDEO_PREV 242 /* drive previous video source */ #define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */ +#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual + brightness control is off, + rely on ambient */ +#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO #define KEY_DISPLAY_OFF 245 /* display device to off state */ #define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ @@ -629,6 +632,7 @@ struct input_keymap_entry { #define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ #define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ #define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE #define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ #define KEY_LOGOFF 0x1b1 /* AL Logoff */ @@ -720,6 +724,24 @@ struct input_keymap_entry { #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ +#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ +#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ +#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ +#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ +#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ +#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ +#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ + +#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ +#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ + +#define KEY_KBDINPUTASSIST_PREV 0x260 +#define KEY_KBDINPUTASSIST_NEXT 0x261 +#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 +#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 +#define KEY_KBDINPUTASSIST_ACCEPT 0x264 +#define KEY_KBDINPUTASSIST_CANCEL 0x265 + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 From 67208c0b1acd7b6db8a853994f7e7c9be94e0178 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 09:54:29 +1000 Subject: [PATCH 009/165] evdev: fix crash for missing ABS_X/Y libevdev_set_abs_info() is a noop if the event code isn't enabled on the device. This leaves ABS_X/Y on NULL, causing a crash later when dereferencing the absinfo. https://bugs.freedesktop.org/show_bug.cgi?id=89783 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index a972b9d0..115dc997 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1444,9 +1444,9 @@ evdev_fix_android_mt(struct evdev_device *device) !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) return; - libevdev_set_abs_info(evdev, ABS_X, + libevdev_enable_event_code(evdev, EV_ABS, ABS_X, libevdev_get_abs_info(evdev, ABS_MT_POSITION_X)); - libevdev_set_abs_info(evdev, ABS_Y, + libevdev_enable_event_code(evdev, EV_ABS, ABS_Y, libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y)); } From e2b13e602220a906584b21d51bc5472577be4372 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 09:54:30 +1000 Subject: [PATCH 010/165] evdev: fix handling of fake MT devices without ABS_X/Y The previous code didn't handle fake MT devices without ABS_X/Y axes (like the Razer BlackWidow keyboard). Those devices usually start at ABS_MISC and go up to ABS_MAX, thus triggering the Android check. Split the condition up: if the device is not a fake MT device we check for the Android missing axes first and add them. Then we proceed, but now we know that the ABS_X axis must exist on any valid device. https://bugs.freedesktop.org/show_bug.cgi?id=89783 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 115dc997..16ebb9ca 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1431,6 +1431,18 @@ evdev_device_get_udev_tags(struct evdev_device *device, return tags; } +/* Fake MT devices have the ABS_MT_SLOT bit set because of + the limited ABS_* range - they aren't MT devices, they + just have too many ABS_ axes */ +static inline bool +evdev_is_fake_mt_device(struct evdev_device *device) +{ + struct libevdev *evdev = device->evdev; + + return libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) && + libevdev_get_num_slots(evdev) == -1; +} + static inline void evdev_fix_android_mt(struct evdev_device *device) { @@ -1441,7 +1453,8 @@ evdev_fix_android_mt(struct evdev_device *device) return; if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) || - !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) + !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) || + evdev_is_fake_mt_device(device)) return; libevdev_enable_event_code(evdev, EV_ABS, ABS_X, @@ -1611,10 +1624,10 @@ evdev_configure_device(struct evdev_device *device) return -1; } - if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) || - libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) { + if (!evdev_is_fake_mt_device(device)) evdev_fix_android_mt(device); + if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) { if (evdev_fix_abs_resolution(device, ABS_X, ABS_Y, @@ -1624,11 +1637,7 @@ evdev_configure_device(struct evdev_device *device) device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X); device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y); - /* Fake MT devices have the ABS_MT_SLOT bit set because of - the limited ABS_* range - they aren't MT devices, they - just have too many ABS_ axes */ - if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) && - libevdev_get_num_slots(evdev) == -1) { + if (evdev_is_fake_mt_device(device)) { udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN; } else if (evdev_configure_mt_device(device) == -1) { return -1; From 55823f23502ae8b427f5b684bc314e0b826d3f1e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 09:54:31 +1000 Subject: [PATCH 011/165] test: adjust scroll wheel test for HWHEEL-only devices Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/pointer.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/pointer.c b/test/pointer.c index 745af638..a39cb2c7 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -396,11 +396,17 @@ START_TEST(pointer_scroll_wheel) litest_drain_events(dev->libinput); - test_wheel_event(dev, REL_WHEEL, -1); - test_wheel_event(dev, REL_WHEEL, 1); + /* make sure we hit at least one of the below two conditions */ + ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) || + libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)); - test_wheel_event(dev, REL_WHEEL, -5); - test_wheel_event(dev, REL_WHEEL, 6); + if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) { + test_wheel_event(dev, REL_WHEEL, -1); + test_wheel_event(dev, REL_WHEEL, 1); + + test_wheel_event(dev, REL_WHEEL, -5); + test_wheel_event(dev, REL_WHEEL, 6); + } if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) { test_wheel_event(dev, REL_HWHEEL, -1); @@ -446,11 +452,17 @@ START_TEST(pointer_scroll_natural_wheel) libinput_device_config_scroll_set_natural_scroll_enabled(device, 1); - test_wheel_event(dev, REL_WHEEL, -1); - test_wheel_event(dev, REL_WHEEL, 1); + /* make sure we hit at least one of the below two conditions */ + ck_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) || + libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)); - test_wheel_event(dev, REL_WHEEL, -5); - test_wheel_event(dev, REL_WHEEL, 6); + if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) { + test_wheel_event(dev, REL_WHEEL, -1); + test_wheel_event(dev, REL_WHEEL, 1); + + test_wheel_event(dev, REL_WHEEL, -5); + test_wheel_event(dev, REL_WHEEL, 6); + } if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL)) { test_wheel_event(dev, REL_HWHEEL, -1); From e2f61b8fb74a06b86abd02caa4cdfde5edc53a32 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 09:54:32 +1000 Subject: [PATCH 012/165] evdev: if a keyboard has a scroll wheel, allow natural scrolling Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 16ebb9ca..a394afa2 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1679,6 +1679,11 @@ evdev_configure_device(struct evdev_device *device) log_info(libinput, "input device '%s', %s is a keyboard\n", device->devname, devnode); + + /* want natural-scroll config option */ + if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) || + libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) + device->scroll.natural_scrolling_enabled = true; } if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) { From 7ea7746c77af79d65a078f1714d1d1db26aa3e5c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 09:54:33 +1000 Subject: [PATCH 013/165] test: add the Razer BlackWidow keyboard device This device has a couple of axes (fake MT) and a wheel. The device itself exports 4 nodes, this device is the second node, the most problematic one. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 1 + test/litest-keyboard-razer-blackwidow.c | 350 ++++++++++++++++++++++++ test/litest.c | 2 + test/litest.h | 1 + 4 files changed, 354 insertions(+) create mode 100644 test/litest-keyboard-razer-blackwidow.c diff --git a/test/Makefile.am b/test/Makefile.am index 38d632de..8fdb4cac 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,7 @@ liblitest_la_SOURCES = \ litest-bcm5974.c \ litest-generic-singletouch.c \ litest-keyboard.c \ + litest-keyboard-razer-blackwidow.c \ litest-mouse.c \ litest-ms-surface-cover.c \ litest-protocol-a-touch-screen.c \ diff --git a/test/litest-keyboard-razer-blackwidow.c b/test/litest-keyboard-razer-blackwidow.c new file mode 100644 index 00000000..b1e95b3a --- /dev/null +++ b/test/litest-keyboard-razer-blackwidow.c @@ -0,0 +1,350 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "litest.h" +#include "litest-int.h" + +/* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=89783 + * This is the second of 4 devices exported by this keyboard, the first is + * just a basic keyboard that is identical to the normal litest-keyboard.c + * file. + */ + +static void litest_blackwidow_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_KEYBOARD_BLACKWIDOW); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x1532, + .product = 0x11b, +}; + +static int events[] = { + EV_REL, REL_HWHEEL, + EV_KEY, KEY_ESC, + EV_KEY, KEY_1, + EV_KEY, KEY_2, + EV_KEY, KEY_3, + EV_KEY, KEY_4, + EV_KEY, KEY_5, + EV_KEY, KEY_6, + EV_KEY, KEY_7, + EV_KEY, KEY_8, + EV_KEY, KEY_9, + EV_KEY, KEY_0, + EV_KEY, KEY_MINUS, + EV_KEY, KEY_EQUAL, + EV_KEY, KEY_BACKSPACE, + EV_KEY, KEY_TAB, + EV_KEY, KEY_Q, + EV_KEY, KEY_W, + EV_KEY, KEY_E, + EV_KEY, KEY_R, + EV_KEY, KEY_T, + EV_KEY, KEY_Y, + EV_KEY, KEY_U, + EV_KEY, KEY_I, + EV_KEY, KEY_O, + EV_KEY, KEY_P, + EV_KEY, KEY_LEFTBRACE, + EV_KEY, KEY_RIGHTBRACE, + EV_KEY, KEY_ENTER, + EV_KEY, KEY_LEFTCTRL, + EV_KEY, KEY_A, + EV_KEY, KEY_S, + EV_KEY, KEY_D, + EV_KEY, KEY_F, + EV_KEY, KEY_G, + EV_KEY, KEY_H, + EV_KEY, KEY_J, + EV_KEY, KEY_K, + EV_KEY, KEY_L, + EV_KEY, KEY_SEMICOLON, + EV_KEY, KEY_APOSTROPHE, + EV_KEY, KEY_GRAVE, + EV_KEY, KEY_LEFTSHIFT, + EV_KEY, KEY_BACKSLASH, + EV_KEY, KEY_Z, + EV_KEY, KEY_X, + EV_KEY, KEY_C, + EV_KEY, KEY_V, + EV_KEY, KEY_B, + EV_KEY, KEY_N, + EV_KEY, KEY_M, + EV_KEY, KEY_COMMA, + EV_KEY, KEY_DOT, + EV_KEY, KEY_SLASH, + EV_KEY, KEY_RIGHTSHIFT, + EV_KEY, KEY_KPASTERISK, + EV_KEY, KEY_LEFTALT, + EV_KEY, KEY_SPACE, + EV_KEY, KEY_CAPSLOCK, + EV_KEY, KEY_F1, + EV_KEY, KEY_F2, + EV_KEY, KEY_F3, + EV_KEY, KEY_F4, + EV_KEY, KEY_F5, + EV_KEY, KEY_F6, + EV_KEY, KEY_F7, + EV_KEY, KEY_F8, + EV_KEY, KEY_F9, + EV_KEY, KEY_F10, + EV_KEY, KEY_NUMLOCK, + EV_KEY, KEY_SCROLLLOCK, + EV_KEY, KEY_KP7, + EV_KEY, KEY_KP8, + EV_KEY, KEY_KP9, + EV_KEY, KEY_KPMINUS, + EV_KEY, KEY_KP4, + EV_KEY, KEY_KP5, + EV_KEY, KEY_KP6, + EV_KEY, KEY_KPPLUS, + EV_KEY, KEY_KP1, + EV_KEY, KEY_KP2, + EV_KEY, KEY_KP3, + EV_KEY, KEY_KP0, + EV_KEY, KEY_KPDOT, + EV_KEY, KEY_ZENKAKUHANKAKU, + EV_KEY, KEY_102ND, + EV_KEY, KEY_F11, + EV_KEY, KEY_F12, + EV_KEY, KEY_RO, + EV_KEY, KEY_KATAKANA, + EV_KEY, KEY_HIRAGANA, + EV_KEY, KEY_HENKAN, + EV_KEY, KEY_KATAKANAHIRAGANA, + EV_KEY, KEY_MUHENKAN, + EV_KEY, KEY_KPJPCOMMA, + EV_KEY, KEY_KPENTER, + EV_KEY, KEY_RIGHTCTRL, + EV_KEY, KEY_KPSLASH, + EV_KEY, KEY_SYSRQ, + EV_KEY, KEY_RIGHTALT, + EV_KEY, KEY_HOME, + EV_KEY, KEY_UP, + EV_KEY, KEY_PAGEUP, + EV_KEY, KEY_LEFT, + EV_KEY, KEY_RIGHT, + EV_KEY, KEY_END, + EV_KEY, KEY_DOWN, + EV_KEY, KEY_PAGEDOWN, + EV_KEY, KEY_INSERT, + EV_KEY, KEY_DELETE, + EV_KEY, KEY_MUTE, + EV_KEY, KEY_VOLUMEDOWN, + EV_KEY, KEY_VOLUMEUP, + EV_KEY, KEY_POWER, + EV_KEY, KEY_KPEQUAL, + EV_KEY, KEY_PAUSE, + EV_KEY, KEY_KPCOMMA, + EV_KEY, KEY_HANGEUL, + EV_KEY, KEY_HANJA, + EV_KEY, KEY_YEN, + EV_KEY, KEY_LEFTMETA, + EV_KEY, KEY_RIGHTMETA, + EV_KEY, KEY_COMPOSE, + EV_KEY, KEY_STOP, + EV_KEY, KEY_AGAIN, + EV_KEY, KEY_PROPS, + EV_KEY, KEY_UNDO, + EV_KEY, KEY_FRONT, + EV_KEY, KEY_COPY, + EV_KEY, KEY_OPEN, + EV_KEY, KEY_PASTE, + EV_KEY, KEY_FIND, + EV_KEY, KEY_CUT, + EV_KEY, KEY_HELP, + EV_KEY, KEY_MENU, + EV_KEY, KEY_CALC, + EV_KEY, KEY_SLEEP, + EV_KEY, KEY_WAKEUP, + EV_KEY, KEY_FILE, + EV_KEY, KEY_WWW, + EV_KEY, KEY_COFFEE, + EV_KEY, KEY_MAIL, + EV_KEY, KEY_BOOKMARKS, + EV_KEY, KEY_BACK, + EV_KEY, KEY_FORWARD, + EV_KEY, KEY_EJECTCD, + EV_KEY, KEY_NEXTSONG, + EV_KEY, KEY_PLAYPAUSE, + EV_KEY, KEY_PREVIOUSSONG, + EV_KEY, KEY_STOPCD, + EV_KEY, KEY_RECORD, + EV_KEY, KEY_REWIND, + EV_KEY, KEY_PHONE, + EV_KEY, KEY_CONFIG, + EV_KEY, KEY_HOMEPAGE, + EV_KEY, KEY_REFRESH, + EV_KEY, KEY_EXIT, + EV_KEY, KEY_EDIT, + EV_KEY, KEY_SCROLLUP, + EV_KEY, KEY_SCROLLDOWN, + EV_KEY, KEY_KPLEFTPAREN, + EV_KEY, KEY_KPRIGHTPAREN, + EV_KEY, KEY_NEW, + EV_KEY, KEY_F13, + EV_KEY, KEY_F14, + EV_KEY, KEY_F15, + EV_KEY, KEY_F16, + EV_KEY, KEY_F17, + EV_KEY, KEY_F18, + EV_KEY, KEY_F19, + EV_KEY, KEY_F20, + EV_KEY, KEY_F21, + EV_KEY, KEY_F22, + EV_KEY, KEY_F23, + EV_KEY, KEY_F24, + EV_KEY, KEY_CLOSE, + EV_KEY, KEY_PLAY, + EV_KEY, KEY_FASTFORWARD, + EV_KEY, KEY_BASSBOOST, + EV_KEY, KEY_PRINT, + EV_KEY, KEY_CAMERA, + EV_KEY, KEY_CHAT, + EV_KEY, KEY_SEARCH, + EV_KEY, KEY_FINANCE, + EV_KEY, KEY_BRIGHTNESSDOWN, + EV_KEY, KEY_BRIGHTNESSUP, + EV_KEY, KEY_KBDILLUMTOGGLE, + EV_KEY, KEY_SAVE, + EV_KEY, KEY_DOCUMENTS, + EV_KEY, KEY_UNKNOWN, + EV_KEY, KEY_VIDEO_NEXT, + EV_KEY, KEY_BRIGHTNESS_AUTO, + EV_KEY, BTN_0, + EV_KEY, KEY_SELECT, + EV_KEY, KEY_GOTO, + EV_KEY, KEY_INFO, + EV_KEY, KEY_PROGRAM, + EV_KEY, KEY_PVR, + EV_KEY, KEY_SUBTITLE, + EV_KEY, KEY_ZOOM, + EV_KEY, KEY_KEYBOARD, + EV_KEY, KEY_PC, + EV_KEY, KEY_TV, + EV_KEY, KEY_TV2, + EV_KEY, KEY_VCR, + EV_KEY, KEY_VCR2, + EV_KEY, KEY_SAT, + EV_KEY, KEY_CD, + EV_KEY, KEY_TAPE, + EV_KEY, KEY_TUNER, + EV_KEY, KEY_PLAYER, + EV_KEY, KEY_DVD, + EV_KEY, KEY_AUDIO, + EV_KEY, KEY_VIDEO, + EV_KEY, KEY_MEMO, + EV_KEY, KEY_CALENDAR, + EV_KEY, KEY_RED, + EV_KEY, KEY_GREEN, + EV_KEY, KEY_YELLOW, + EV_KEY, KEY_BLUE, + EV_KEY, KEY_CHANNELUP, + EV_KEY, KEY_CHANNELDOWN, + EV_KEY, KEY_LAST, + EV_KEY, KEY_NEXT, + EV_KEY, KEY_RESTART, + EV_KEY, KEY_SLOW, + EV_KEY, KEY_SHUFFLE, + EV_KEY, KEY_PREVIOUS, + EV_KEY, KEY_VIDEOPHONE, + EV_KEY, KEY_GAMES, + EV_KEY, KEY_ZOOMIN, + EV_KEY, KEY_ZOOMOUT, + EV_KEY, KEY_ZOOMRESET, + EV_KEY, KEY_WORDPROCESSOR, + EV_KEY, KEY_EDITOR, + EV_KEY, KEY_SPREADSHEET, + EV_KEY, KEY_GRAPHICSEDITOR, + EV_KEY, KEY_PRESENTATION, + EV_KEY, KEY_DATABASE, + EV_KEY, KEY_NEWS, + EV_KEY, KEY_VOICEMAIL, + EV_KEY, KEY_ADDRESSBOOK, + EV_KEY, KEY_MESSENGER, + EV_KEY, KEY_DISPLAYTOGGLE, + EV_KEY, KEY_SPELLCHECK, + EV_KEY, KEY_LOGOFF, + EV_KEY, KEY_MEDIA_REPEAT, + EV_KEY, KEY_IMAGES, + EV_KEY, KEY_BUTTONCONFIG, + EV_KEY, KEY_TASKMANAGER, + EV_KEY, KEY_JOURNAL, + EV_KEY, KEY_CONTROLPANEL, + EV_KEY, KEY_APPSELECT, + EV_KEY, KEY_SCREENSAVER, + EV_KEY, KEY_VOICECOMMAND, + EV_KEY, KEY_BRIGHTNESS_MIN, + EV_KEY, KEY_BRIGHTNESS_MAX, + EV_MSC, MSC_SCAN, + -1 , -1, +}; + +static struct input_absinfo absinfo[] = { + { ABS_VOLUME, 0, 572, 0, 0, 0 }, + { ABS_MISC, 0, 255, 0, 0, 0 }, + { 0x29, 0, 255, 0, 0, 0 }, + { 0x2a, 0, 255, 0, 0, 0 }, + { 0x2b, 0, 255, 0, 0, 0 }, + { 0x2c, 0, 255, 0, 0, 0 }, + { 0x2d, 0, 255, 0, 0, 0 }, + { 0x2e, 0, 255, 0, 0, 0 }, + { 0x2f, 0, 255, 0, 0, 0 }, + { 0x30, 0, 255, 0, 0, 0 }, + { 0x31, 0, 255, 0, 0, 0 }, + { 0x32, 0, 255, 0, 0, 0 }, + { 0x33, 0, 255, 0, 0, 0 }, + { 0x34, 0, 255, 0, 0, 0 }, + { 0x35, 0, 255, 0, 0, 0 }, + { 0x36, 0, 255, 0, 0, 0 }, + { 0x37, 0, 255, 0, 0, 0 }, + { 0x38, 0, 255, 0, 0, 0 }, + { 0x39, 0, 255, 0, 0, 0 }, + { 0x3a, 0, 255, 0, 0, 0 }, + { 0x3b, 0, 255, 0, 0, 0 }, + { 0x3c, 0, 255, 0, 0, 0 }, + { 0x3d, 0, 255, 0, 0, 0 }, + { 0x3e, 0, 255, 0, 0, 0 }, + { 0x3f, 0, 255, 0, 0, 0 }, + { .value = -1 }, +}; + +struct litest_test_device litest_keyboard_blackwidow_device = { + .type = LITEST_KEYBOARD_BLACKWIDOW, + .features = LITEST_KEYS | LITEST_WHEEL, + .shortname = "blackwidow", + .setup = litest_blackwidow_setup, + .interface = NULL, + + .name = "Razer Razer BlackWidow 2013", + .id = &input_id, + .absinfo = absinfo, + .events = events, +}; diff --git a/test/litest.c b/test/litest.c index e6ae1617..b1918e21 100644 --- a/test/litest.c +++ b/test/litest.c @@ -98,6 +98,7 @@ extern struct litest_test_device litest_synaptics_hover_device; extern struct litest_test_device litest_synaptics_carbon3rd_device; extern struct litest_test_device litest_protocol_a_screen; extern struct litest_test_device litest_wacom_finger_device; +extern struct litest_test_device litest_keyboard_blackwidow_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -117,6 +118,7 @@ struct litest_test_device* devices[] = { &litest_synaptics_carbon3rd_device, &litest_protocol_a_screen, &litest_wacom_finger_device, + &litest_keyboard_blackwidow_device, NULL, }; diff --git a/test/litest.h b/test/litest.h index 676293be..d2ed3d03 100644 --- a/test/litest.h +++ b/test/litest.h @@ -53,6 +53,7 @@ enum litest_device_type { LITEST_SYNAPTICS_TRACKPOINT_BUTTONS = -17, LITEST_PROTOCOL_A_SCREEN = -18, LITEST_WACOM_FINGER = -19, + LITEST_KEYBOARD_BLACKWIDOW = -20, }; enum litest_device_feature { From 591a41f9ddbdcceb9f3f80e3b2fb8b173e1d3c81 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 13:54:57 +1000 Subject: [PATCH 014/165] touchpad: count the tapping fingers separately from the main touchpad code tp->nfingers_down gives us the current state of the touchpad but in the case of the tapping state we need the touchpoints separately. If all touchpoints end in the same SYN_REPORT frame, tp->nfingers_down is 0 when we handle the touch releases. This changes the tap state to IDLE on the first release and then logs a bug when the remaining touches are released while the touchpad is in IDLE. Avoid this by counting the fingers separately for the tap state, this way we can count up/down with the down/up events as we process them for the tapping state machine. This also adds tests for 4 and 5-finger tapping which is how the bug was discovered in the first place. https://bugs.freedesktop.org/show_bug.cgi?id=89800 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 5 +- src/evdev-mt-touchpad.h | 1 + test/touchpad.c | 233 ++++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 4ba4ad28..78d4a0f1 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -452,7 +452,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_RELEASE: - if (tp->nfingers_down == 0) + if (tp->tap.tap_finger_count == 0) tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_TOUCH: @@ -567,10 +567,12 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.state = TAP_TOUCH_STATE_DEAD; if (t->state == TOUCH_BEGIN) { + tp->tap.tap_finger_count++; t->tap.state = TAP_TOUCH_STATE_TOUCH; t->tap.initial = t->point; tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); } else if (t->state == TOUCH_END) { + tp->tap.tap_finger_count--; 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 && @@ -754,6 +756,7 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) } tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE; + tp->tap.tap_finger_count = 0; } void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 19a262e3..b88dadd9 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -255,6 +255,7 @@ struct tp_dispatch { struct libinput_timer timer; enum tp_tap_state state; uint32_t buttons_pressed; + unsigned int tap_finger_count; } tap; struct { diff --git a/test/touchpad.c b/test/touchpad.c index 6fa23016..b06e00dc 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -369,6 +369,38 @@ START_TEST(touchpad_2fg_tap_inverted) } END_TEST +START_TEST(touchpad_2fg_tap_quickrelease) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(dev->libinput); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 70); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_1fg_tap_click) { struct litest_device *dev = litest_current_device(); @@ -705,6 +737,46 @@ START_TEST(touchpad_3fg_tap) } END_TEST +START_TEST(touchpad_3fg_tap_quickrelease) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (libevdev_get_abs_maximum(dev->evdev, + ABS_MT_SLOT) <= 2) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_touch_down(dev, 2, 80, 50); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 2); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + libinput_dispatch(li); + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_3fg_tap_btntool) { struct litest_device *dev = litest_current_device(); @@ -783,6 +855,161 @@ START_TEST(touchpad_3fg_tap_btntool_inverted) } END_TEST +START_TEST(touchpad_4fg_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i; + + if (libevdev_get_abs_maximum(dev->evdev, + ABS_MT_SLOT) <= 3) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + for (i = 0; i < 4; i++) { + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_touch_down(dev, 2, 80, 50); + litest_touch_down(dev, 3, 90, 50); + + litest_touch_up(dev, (i + 3) % 4); + litest_touch_up(dev, (i + 2) % 4); + litest_touch_up(dev, (i + 1) % 4); + litest_touch_up(dev, (i + 0) % 4); + + libinput_dispatch(li); + litest_assert_empty_queue(li); + litest_timeout_tap(); + litest_assert_empty_queue(li); + event = libinput_get_event(li); + ck_assert(event == NULL); + } +} +END_TEST + +START_TEST(touchpad_4fg_tap_quickrelease) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (libevdev_get_abs_maximum(dev->evdev, + ABS_MT_SLOT) <= 3) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 70, 50); + litest_touch_down(dev, 2, 80, 50); + litest_touch_down(dev, 3, 90, 50); + + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 2); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 3); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_QUADTAP, 0); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + litest_assert_empty_queue(li); + litest_timeout_tap(); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_5fg_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + int i; + + if (libevdev_get_abs_maximum(dev->evdev, + ABS_MT_SLOT) <= 4) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + for (i = 0; i < 5; i++) { + litest_drain_events(li); + + litest_touch_down(dev, 0, 20, 50); + litest_touch_down(dev, 1, 30, 50); + litest_touch_down(dev, 2, 40, 50); + litest_touch_down(dev, 3, 50, 50); + litest_touch_down(dev, 4, 60, 50); + + litest_touch_up(dev, (i + 4) % 5); + litest_touch_up(dev, (i + 3) % 5); + litest_touch_up(dev, (i + 2) % 5); + litest_touch_up(dev, (i + 1) % 5); + litest_touch_up(dev, (i + 0) % 5); + + libinput_dispatch(li); + litest_assert_empty_queue(li); + litest_timeout_tap(); + litest_assert_empty_queue(li); + event = libinput_get_event(li); + ck_assert(event == NULL); + } +} +END_TEST + +START_TEST(touchpad_5fg_tap_quickrelease) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (libevdev_get_abs_maximum(dev->evdev, + ABS_MT_SLOT) <= 4) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 20, 50); + litest_touch_down(dev, 1, 30, 50); + litest_touch_down(dev, 2, 40, 50); + litest_touch_down(dev, 3, 70, 50); + litest_touch_down(dev, 4, 90, 50); + + litest_event(dev, EV_ABS, ABS_MT_SLOT, 0); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 1); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 2); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 3); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_ABS, ABS_MT_SLOT, 4); + litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + litest_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, 0); + litest_event(dev, EV_KEY, BTN_TOUCH, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + litest_assert_empty_queue(li); + litest_timeout_tap(); + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_click_defaults_clickfinger) { struct litest_device *dev = litest_current_device(); @@ -3370,6 +3597,7 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_2fg_tap_n_drag_3fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_2fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); litest_add("touchpad:tap", touchpad_2fg_tap_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:tap", touchpad_2fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); litest_add("touchpad:tap", touchpad_1fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:tap", touchpad_2fg_tap_click, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD); @@ -3381,6 +3609,11 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", touchpad_3fg_tap_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_3fg_tap_btntool_inverted, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_3fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:tap", touchpad_3fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:tap", touchpad_4fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:tap", touchpad_4fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:tap", touchpad_5fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); + litest_add("touchpad:tap", touchpad_5fg_tap_quickrelease, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_SEMI_MT); /* Real buttons don't interfere with tapping, so don't run those for pads with buttons */ From c6d81c84dec9d0ea1ee491563c0116ba5e9ea8a7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 16:05:15 +1000 Subject: [PATCH 015/165] evdev: fix inverted mouse normalization Regression introduced in 9f8edc5fd880e0a9c482b36e6b4120ccc056ee0b where it changed from delta / (dpi/default) to delta * dpi/default, causing the inverse effect of what the dpi setting is supposed to achieve. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index a394afa2..a6d6fae8 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -230,8 +230,8 @@ normalize_delta(struct evdev_device *device, const struct device_coords *delta, struct normalized_coords *normalized) { - normalized->x = delta->x * (double)device->dpi / DEFAULT_MOUSE_DPI; - normalized->y = delta->y * (double)device->dpi / DEFAULT_MOUSE_DPI; + normalized->x = delta->x * DEFAULT_MOUSE_DPI / (double)device->dpi; + normalized->y = delta->y * DEFAULT_MOUSE_DPI / (double)device->dpi; } static void From ec0b927c5b7843e6c62f31e8dc6c5b3ccf03b246 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 14:52:44 +1000 Subject: [PATCH 016/165] touchpad: remove trailing semicolon from macro Avoids empty statements Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 78d4a0f1..b99249bf 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -34,7 +34,7 @@ #include "evdev-mt-touchpad.h" -#define CASE_RETURN_STRING(a) case a: return #a; +#define CASE_RETURN_STRING(a) case a: return #a #define DEFAULT_TAP_TIMEOUT_PERIOD 180 #define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3) From 08aee12be4ecc00c9ef0cbc0a0df0a8eca14ad1f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 14:54:39 +1000 Subject: [PATCH 017/165] touchpad: add state debugging to the edge scroll state machine Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-edge-scroll.c | 39 ++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index 0cdbb19d..369fdedc 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -29,6 +29,8 @@ #include "evdev-mt-touchpad.h" +#define CASE_RETURN_STRING(a) case a: return #a + #define DEFAULT_SCROLL_LOCK_TIMEOUT 300 /* ms */ /* Use a reasonably large threshold until locked into scrolling mode, to avoid accidentally locking in scrolling mode when trying to use the entire @@ -44,6 +46,32 @@ enum scroll_event { SCROLL_EVENT_POSTED, }; +static inline const char* +edge_state_to_str(enum tp_edge_scroll_touch_state state) +{ + + switch (state) { + CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_NONE); + CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE_NEW); + CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_EDGE); + CASE_RETURN_STRING(EDGE_SCROLL_TOUCH_STATE_AREA); + } + return NULL; +} + +static inline const char* +edge_event_to_str(enum scroll_event event) +{ + switch (event) { + CASE_RETURN_STRING(SCROLL_EVENT_TOUCH); + CASE_RETURN_STRING(SCROLL_EVENT_MOTION); + CASE_RETURN_STRING(SCROLL_EVENT_RELEASE); + CASE_RETURN_STRING(SCROLL_EVENT_TIMEOUT); + CASE_RETURN_STRING(SCROLL_EVENT_POSTED); + } + return NULL; +} + static uint32_t tp_touch_get_edge(struct tp_dispatch *tp, struct tp_touch *t) { @@ -204,7 +232,10 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum scroll_event event) { - switch (t->scroll.edge_state) { + struct libinput *libinput = tp->device->base.seat->libinput; + enum tp_edge_scroll_touch_state current = t->scroll.edge_state; + + switch (current) { case EDGE_SCROLL_TOUCH_STATE_NONE: tp_edge_scroll_handle_none(tp, t, event); break; @@ -218,6 +249,12 @@ tp_edge_scroll_handle_event(struct tp_dispatch *tp, tp_edge_scroll_handle_area(tp, t, event); break; } + + log_debug(libinput, + "edge state: %s → %s → %s\n", + edge_state_to_str(current), + edge_event_to_str(event), + edge_state_to_str(t->scroll.edge_state)); } static void From ac0f5e799aec72ab392f6d79bbd97662000335e7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 8 Apr 2015 17:00:50 +1000 Subject: [PATCH 018/165] touchpad: delay fake finger processing until the EV_SYN A switch from BTN_TOOL_FINGER to BTN_TOOL_DOUBLETAP sets the former to 0, then the latter to 1, within the same frame. In the previous code we'd end the first touchpoint, then start two new ones immediately after when the DOUBLETAP comes in. This causes bug notices in the edge scrolling code and finger miscounts in the tapping code (since neither processes the change, there is no SYN_REPORT between the two). Only update the state bits when we get the events, handle the fake touch sequence start/end on SYN_REPORT instead. https://bugs.freedesktop.org/show_bug.cgi?id=89949 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 7d6d838e..724fd326 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -317,16 +317,13 @@ tp_process_absolute_st(struct tp_dispatch *tp, } static void -tp_process_fake_touch(struct tp_dispatch *tp, - const struct input_event *e, - uint64_t time) +tp_process_fake_touches(struct tp_dispatch *tp, + uint64_t time) { struct tp_touch *t; unsigned int nfake_touches; unsigned int i, start; - tp_fake_finger_set(tp, e->code, e->value != 0); - nfake_touches = tp_fake_finger_count(tp); start = tp->has_mt ? tp->real_touches : 0; @@ -390,7 +387,7 @@ tp_process_key(struct tp_dispatch *tp, case BTN_TOOL_DOUBLETAP: case BTN_TOOL_TRIPLETAP: case BTN_TOOL_QUADTAP: - tp_process_fake_touch(tp, e, time); + tp_fake_finger_set(tp, e->code, !!e->value); break; case BTN_0: case BTN_1: @@ -548,6 +545,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) struct tp_touch *first = tp_get_touch(tp, 0); unsigned int i; + tp_process_fake_touches(tp, time); tp_unhover_touches(tp, time); for (i = 0; i < tp->ntouches; i++) { From 36ec8b513383820a4729f7df4bc7b439ed0eb3d5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 9 Apr 2015 07:51:51 +1000 Subject: [PATCH 019/165] evdev: reject devices with only one of x/y resolution This is a kernel bug, reject such devices outright. This saves us from a bunch of extra double checks to make sure that the resolutions are always set up. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 32 ++++++++++++++----- test/device.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index a6d6fae8..243cd222 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1377,13 +1377,6 @@ evdev_fix_abs_resolution(struct evdev_device *device, absx = libevdev_get_abs_info(evdev, xcode); absy = libevdev_get_abs_info(evdev, ycode); - if ((absx->resolution == 0 && absy->resolution != 0) || - (absx->resolution != 0 && absy->resolution == 0)) { - log_bug_kernel(libinput, - "Kernel has only x or y resolution, not both.\n"); - return 0; - } - if (absx->resolution == 0 || absx->resolution == EVDEV_FAKE_RESOLUTION) { fixed = *absx; fixed.resolution = xresolution; @@ -1487,8 +1480,10 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code) static int evdev_reject_device(struct evdev_device *device) { + struct libinput *libinput = device->base.seat->libinput; struct libevdev *evdev = device->evdev; unsigned int code; + const struct input_absinfo *absx, *absy; if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ^ libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) @@ -1498,6 +1493,29 @@ evdev_reject_device(struct evdev_device *device) libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) return -1; + if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) { + absx = libevdev_get_abs_info(evdev, ABS_X); + absy = libevdev_get_abs_info(evdev, ABS_Y); + if ((absx->resolution == 0 && absy->resolution != 0) || + (absx->resolution != 0 && absy->resolution == 0)) { + log_bug_kernel(libinput, + "Kernel has only x or y resolution, not both.\n"); + return -1; + } + } + + if (!evdev_is_fake_mt_device(device) && + libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X)) { + absx = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X); + absy = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y); + if ((absx->resolution == 0 && absy->resolution != 0) || + (absx->resolution != 0 && absy->resolution == 0)) { + log_bug_kernel(libinput, + "Kernel has only x or y MT resolution, not both.\n"); + return -1; + } + } + for (code = 0; code < ABS_CNT; code++) { switch (code) { case ABS_MISC: diff --git a/test/device.c b/test/device.c index c9d52556..cf9885ad 100644 --- a/test/device.c +++ b/test/device.c @@ -894,6 +894,91 @@ START_TEST(abs_mt_device_no_range) } END_TEST +START_TEST(abs_device_missing_res) +{ + struct libevdev_uinput *uinput; + struct libinput *li; + struct libinput_device *device; + struct input_absinfo absinfo[] = { + { ABS_X, 0, 10, 0, 0, 10 }, + { ABS_Y, 0, 10, 0, 0, 0 }, + { -1, -1, -1, -1, -1, -1 } + }; + + li = litest_create_context(); + litest_disable_log_handler(li); + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + device = libinput_path_add_device(li, + libevdev_uinput_get_devnode(uinput)); + ck_assert(device == NULL); + libevdev_uinput_destroy(uinput); + + absinfo[0].resolution = 0; + absinfo[1].resolution = 20; + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + device = libinput_path_add_device(li, + libevdev_uinput_get_devnode(uinput)); + ck_assert(device == NULL); + libevdev_uinput_destroy(uinput); + + litest_restore_log_handler(li); + libinput_unref(li); + +} +END_TEST + +START_TEST(abs_mt_device_missing_res) +{ + struct libevdev_uinput *uinput; + struct libinput *li; + struct libinput_device *device; + struct input_absinfo absinfo[] = { + { ABS_X, 0, 10, 0, 0, 10 }, + { ABS_Y, 0, 10, 0, 0, 10 }, + { ABS_MT_SLOT, 0, 2, 0, 0, 0 }, + { ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 }, + { ABS_MT_POSITION_X, 0, 10, 0, 0, 10 }, + { ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 }, + { -1, -1, -1, -1, -1, -1 } + }; + + li = litest_create_context(); + litest_disable_log_handler(li); + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + device = libinput_path_add_device(li, + libevdev_uinput_get_devnode(uinput)); + ck_assert(device == NULL); + libevdev_uinput_destroy(uinput); + + absinfo[4].resolution = 0; + absinfo[5].resolution = 20; + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + device = libinput_path_add_device(li, + libevdev_uinput_get_devnode(uinput)); + ck_assert(device == NULL); + libevdev_uinput_destroy(uinput); + + litest_restore_log_handler(li); + libinput_unref(li); + +} +END_TEST int main (int argc, char **argv) { litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); @@ -928,6 +1013,8 @@ int main (int argc, char **argv) litest_add_no_device("device:invalid devices", abs_mt_device_no_absy); litest_add_no_device("device:invalid devices", abs_device_no_range); litest_add_no_device("device:invalid devices", abs_mt_device_no_range); + litest_add_no_device("device:invalid devices", abs_device_missing_res); + litest_add_no_device("device:invalid devices", abs_mt_device_missing_res); return litest_run(argc, argv); } From 8b1df055163ab72ef71215fe212ea97852e7d460 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 9 Apr 2015 14:23:16 +1000 Subject: [PATCH 020/165] touchpad: detect fake finger overflow after BTN_TOOL_QUINTTAB Up to QUINTTAP, we count fake fingers through the BTN_TOOL_*TAP kernel defines. Once we exceed QUINTTAP, the nfake_finger count returns to 0 and tp_unhover_touches terminates all touch sequences. The most visible effect of this was stopped in 591a41f but the problem remained. Since we're not using 5 fingers for anything, use that to set the overflow flag. The kernel gives us either a BTN_TOUCH 0 (all released) or a lower BTN_TOOL_*TAP to unset the flag when we go below 5 fingers again. And if we overflow, we can skip the unhovering of touch points since we a) have a decent touchpad that gives us real touchpoints and b) hovering isn't supported for 5 touches anyway. https://bugs.freedesktop.org/show_bug.cgi?id=89800 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 724fd326..19ec99ea 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -34,6 +34,7 @@ #define DEFAULT_ACCEL_NUMERATOR 3000.0 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */ +#define FAKE_FINGER_OVERFLOW (1 << 7) static inline int tp_hysteresis(int in, int center, int margin) @@ -127,8 +128,10 @@ tp_get_touch(struct tp_dispatch *tp, unsigned int slot) static inline unsigned int tp_fake_finger_count(struct tp_dispatch *tp) { - /* don't count BTN_TOUCH */ - return ffs(tp->fake_touches >> 1); + if (tp->fake_touches & FAKE_FINGER_OVERFLOW) + return FAKE_FINGER_OVERFLOW; + else /* don't count BTN_TOUCH */ + return ffs(tp->fake_touches >> 1); } static inline bool @@ -146,6 +149,8 @@ tp_fake_finger_set(struct tp_dispatch *tp, switch (code) { case BTN_TOUCH: + if (!is_press) + tp->fake_touches &= ~FAKE_FINGER_OVERFLOW; shift = 0; break; case BTN_TOOL_FINGER: @@ -156,14 +161,24 @@ tp_fake_finger_set(struct tp_dispatch *tp, case BTN_TOOL_QUADTAP: shift = code - BTN_TOOL_DOUBLETAP + 2; break; + /* when QUINTTAP is released we're either switching to 6 fingers + (flag stays in place until BTN_TOUCH is released) or + one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */ + case BTN_TOOL_QUINTTAP: + if (is_press) + tp->fake_touches |= FAKE_FINGER_OVERFLOW; + return; default: return; } - if (is_press) + if (is_press) { + tp->fake_touches &= ~FAKE_FINGER_OVERFLOW; tp->fake_touches |= 1 << shift; - else + + } else { tp->fake_touches &= ~(0x1 << shift); + } } static inline void @@ -325,6 +340,8 @@ tp_process_fake_touches(struct tp_dispatch *tp, unsigned int i, start; nfake_touches = tp_fake_finger_count(tp); + if (nfake_touches == FAKE_FINGER_OVERFLOW) + return; start = tp->has_mt ? tp->real_touches : 0; for (i = start; i < tp->ntouches; i++) { @@ -387,6 +404,7 @@ tp_process_key(struct tp_dispatch *tp, case BTN_TOOL_DOUBLETAP: case BTN_TOOL_TRIPLETAP: case BTN_TOOL_QUADTAP: + case BTN_TOOL_QUINTTAP: tp_fake_finger_set(tp, e->code, !!e->value); break; case BTN_0: @@ -494,6 +512,9 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) return; nfake_touches = tp_fake_finger_count(tp); + if (nfake_touches == FAKE_FINGER_OVERFLOW) + return; + if (tp->nfingers_down == nfake_touches && ((tp->nfingers_down == 0 && !tp_fake_finger_is_touching(tp)) || (tp->nfingers_down > 0 && tp_fake_finger_is_touching(tp)))) From 753d68aaa762b363a3c19c63889f0f83698dca7d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 9 Apr 2015 15:27:33 +1000 Subject: [PATCH 021/165] touchpad: a touch in TOUCH_NONE doesn't need to be ended Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 19ec99ea..06270569 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -547,7 +547,8 @@ tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) for (i = tp->ntouches - 1; i >= 0; i--) { t = tp_get_touch(tp, i); - if (t->state == TOUCH_HOVERING) + if (t->state == TOUCH_HOVERING || + t->state == TOUCH_NONE) continue; tp_end_touch(tp, t, time); From 44ce633bff69e8ba7436747c601d03d870d65313 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 9 Apr 2015 15:27:34 +1000 Subject: [PATCH 022/165] touchpad: rename real_touches to num_slots Less ambiguous since real_touches can be interpreted to "current number of real touches as opposed to fake touches". Which it isn't, this variable holds the number of slots. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-gestures.c | 2 +- src/evdev-mt-touchpad.c | 10 +++++----- src/evdev-mt-touchpad.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 7dbf4c4a..e01b9218 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -39,7 +39,7 @@ tp_get_touches_delta(struct tp_dispatch *tp, bool average) struct normalized_coords normalized; struct normalized_coords delta = {0.0, 0.0}; - for (i = 0; i < tp->real_touches; i++) { + for (i = 0; i < tp->num_slots; i++) { t = &tp->touches[i]; if (tp_touch_active(tp, t) && t->dirty) { diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 06270569..68070c2e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -343,7 +343,7 @@ tp_process_fake_touches(struct tp_dispatch *tp, if (nfake_touches == FAKE_FINGER_OVERFLOW) return; - start = tp->has_mt ? tp->real_touches : 0; + start = tp->has_mt ? tp->num_slots : 0; for (i = start; i < tp->ntouches; i++) { t = tp_get_touch(tp, i); if (i < nfake_touches) @@ -577,7 +577,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down) tp_motion_history_reset(t); - if (i >= tp->real_touches && t->state != TOUCH_NONE) { + if (i >= tp->num_slots && t->state != TOUCH_NONE) { t->point = first->point; if (!t->dirty) t->dirty = first->dirty; @@ -938,11 +938,11 @@ tp_init_slots(struct tp_dispatch *tp, absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT); if (absinfo) { - tp->real_touches = absinfo->maximum + 1; + tp->num_slots = absinfo->maximum + 1; tp->slot = absinfo->value; tp->has_mt = true; } else { - tp->real_touches = 1; + tp->num_slots = 1; tp->slot = 0; tp->has_mt = false; } @@ -958,7 +958,7 @@ tp_init_slots(struct tp_dispatch *tp, } } - tp->ntouches = max(tp->real_touches, n_btn_tool_touches); + tp->ntouches = max(tp->num_slots, n_btn_tool_touches); tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch)); if (!tp->touches) return -1; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index b88dadd9..6ab09817 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -183,7 +183,7 @@ struct tp_dispatch { bool semi_mt; enum touchpad_model model; - unsigned int real_touches; /* number of slots */ + unsigned int num_slots; /* number of slots */ unsigned int ntouches; /* no slots inc. fakes */ struct tp_touch *touches; /* len == ntouches */ /* bit 0: BTN_TOUCH From c621656790578f3438d0092a6125d3322f74e6e3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 10 Apr 2015 15:45:35 +1000 Subject: [PATCH 023/165] tools: apply the config options for the event-gui helper too This changes the current behavior of tapping enabled-by-default in the GUI helper. Signed-off-by: Peter Hutterer --- tools/event-gui.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tools/event-gui.c b/tools/event-gui.c index 75e4e194..3ce74183 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -279,14 +279,8 @@ handle_event_device_notify(struct libinput_event *ev) libinput_device_get_name(dev), type); - if (libinput_device_config_tap_get_finger_count(dev) > 0) { - enum libinput_config_status status; - status = libinput_device_config_tap_set_enabled(dev, - LIBINPUT_CONFIG_TAP_ENABLED); - if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) - error("%s: Failed to enable tapping\n", - libinput_device_get_sysname(dev)); - } + tools_device_apply_config(libinput_event_get_device(ev), + &options); li = libinput_event_get_context(ev); w = libinput_get_user_data(li); From 2a1a28dd1ddb7c3717e48e508d65a74a4a33f143 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 10 Apr 2015 15:45:59 +1000 Subject: [PATCH 024/165] tools: add commandline flag to control the pointer accel speed Signed-off-by: Peter Hutterer --- tools/shared.c | 15 +++++++++++++++ tools/shared.h | 1 + 2 files changed, 16 insertions(+) diff --git a/tools/shared.c b/tools/shared.c index 2cff52c4..b1690494 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -44,6 +44,7 @@ enum options { OPT_LEFT_HANDED_ENABLE, OPT_LEFT_HANDED_DISABLE, OPT_CLICK_METHOD, + OPT_SPEED, }; static void @@ -71,6 +72,7 @@ tools_usage() "--enable-left-handed\n" "--disable-left-handed.... enable/disable left-handed button configuration\n" "--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n" + "--set-speed=.... set pointer acceleration speed\n" "\n" "These options apply to all applicable devices, if a feature\n" "is not explicitly specified it is left at each device's default.\n" @@ -91,6 +93,7 @@ tools_init_options(struct tools_options *options) options->click_method = -1; options->backend = BACKEND_UDEV; options->seat = "seat0"; + options->speed = 0.0; } int @@ -111,6 +114,7 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) { "enable-left-handed", 0, 0, OPT_LEFT_HANDED_ENABLE }, { "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE }, { "set-click-method", 1, 0, OPT_CLICK_METHOD }, + { "speed", 1, 0, OPT_SPEED }, { 0, 0, 0, 0} }; @@ -176,6 +180,13 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) return 1; } break; + case OPT_SPEED: + if (!optarg) { + tools_usage(); + return 1; + } + options->speed = atof(optarg); + break; default: tools_usage(); return 1; @@ -289,4 +300,8 @@ tools_device_apply_config(struct libinput_device *device, if (options->click_method != -1) libinput_device_config_click_set_method(device, options->click_method); + + if (libinput_device_config_accel_is_available(device)) + libinput_device_config_accel_set_speed(device, + options->speed); } diff --git a/tools/shared.h b/tools/shared.h index fcf748fd..caf48558 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -40,6 +40,7 @@ struct tools_options { int natural_scroll; int left_handed; enum libinput_config_click_method click_method; + double speed; }; void tools_init_options(struct tools_options *options); From 76adf39ab39e8a99f3e4901b23b01a8d4b1f93f3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Apr 2015 16:42:00 +0200 Subject: [PATCH 025/165] filter: Make acceleration range wider The main purpose of this patch is to allow the user to actually slow down pointer movement using libinput_device_config_accel_set_speed, this is achieved by changing the max-accel setting from "2.0 - speed" to "2.0 - speed * 1.5", resulting in a max-accel of 0.5 when the user configures speed at -1.0, the other accel profile parameters are adjusted by the same factor to keep the curve the same. This means that the user can get the exact same behavior as before by multiplying the old setting by 0.6667 (2/3), this also means that this change not only allows the user to select a slower speed, but to keep things balanced the same as before, also a higher speed. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/filter.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/filter.c b/src/filter.c index 033201fc..962d74de 100644 --- a/src/filter.c +++ b/src/filter.c @@ -258,13 +258,15 @@ accelerator_set_speed(struct motion_filter *filter, assert(speed >= -1.0 && speed <= 1.0); /* delay when accel kicks in */ - accel_filter->threshold = DEFAULT_THRESHOLD - speed/6.0; + accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4.0; + if (accel_filter->threshold < 0.2) + accel_filter->threshold = 0.2; /* adjust max accel factor */ - accel_filter->accel = DEFAULT_ACCELERATION + speed; + accel_filter->accel = DEFAULT_ACCELERATION + speed * 1.5; /* higher speed -> faster to reach max */ - accel_filter->incline = DEFAULT_INCLINE + speed/2.0; + accel_filter->incline = DEFAULT_INCLINE + speed * 0.75; filter->speed = speed; return true; From ae1ede741d033d7040afca6c902f7161500b8c16 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 10 Apr 2015 15:37:15 +1000 Subject: [PATCH 026/165] tools: add a tool for printing pointer acceleration parameters Prints the various pointer accel behaviors into a format understood by gnuplot, which then provides prettiness. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/Makefile.am | 10 +- tools/.gitignore | 1 + tools/Makefile.am | 6 +- tools/ptraccel-debug.c | 299 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 tools/ptraccel-debug.c diff --git a/src/Makefile.am b/src/Makefile.am index ff65ff7f..0f180cc4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,6 @@ lib_LTLIBRARIES = libinput.la -noinst_LTLIBRARIES = libinput-util.la +noinst_LTLIBRARIES = libinput-util.la \ + libfilter.la include_HEADERS = \ libinput.h @@ -48,6 +49,13 @@ libinput_util_la_CFLAGS = -I$(top_srcdir)/include \ $(LIBUDEV_CFLAGS) \ $(GCC_CFLAGS) +libfilter_la_SOURCES = \ + filter.c \ + filter.h \ + filter-private.h +libfilter_la_LIBADD = +libfilter_la_CFLAGS = + libinput_la_LDFLAGS = -version-info $(LIBINPUT_LT_VERSION) -shared \ -Wl,--version-script=$(srcdir)/libinput.sym diff --git a/tools/.gitignore b/tools/.gitignore index cf348a6f..6d530e67 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,2 +1,3 @@ event-debug event-gui +ptraccel-debug diff --git a/tools/Makefile.am b/tools/Makefile.am index cebcd729..34d5ab07 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,4 @@ -noinst_PROGRAMS = event-debug +noinst_PROGRAMS = event-debug ptraccel-debug noinst_LTLIBRARIES = libshared.la AM_CPPFLAGS = -I$(top_srcdir)/include \ @@ -14,6 +14,10 @@ event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) event_debug_LDFLAGS = -no-install event_debug_CFLAGS = $(LIBUDEV_CFLAGS) +ptraccel_debug_SOURCES = ptraccel-debug.c +ptraccel_debug_LDADD = ../src/libfilter.la +ptraccel_debug_LDFLAGS = -no-install + if BUILD_EVENTGUI noinst_PROGRAMS += event-gui diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c new file mode 100644 index 00000000..d00e8f37 --- /dev/null +++ b/tools/ptraccel-debug.c @@ -0,0 +1,299 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +print_ptraccel_deltas(struct motion_filter *filter, double step) +{ + struct normalized_coords motion; + uint64_t time = 0; + double i; + + printf("# gnuplot:\n"); + printf("# set xlabel dx unaccelerated\n"); + printf("# set ylabel dx accelerated\n"); + printf("# set style data lines\n"); + printf("# plot \"gnuplot.data\" using 1:2 title \"step %.2f\"\n", step); + printf("#\n"); + + /* Accel flattens out after 15 and becomes linear */ + for (i = 0.0; i < 15.0; i += step) { + motion.x = i; + motion.y = 0; + time += 12; /* pretend 80Hz data */ + + motion = filter_dispatch(filter, &motion, NULL, time); + + printf("%.2f %.3f\n", i, motion.x); + } +} + +static void +print_ptraccel_movement(struct motion_filter *filter, + int nevents, + double max_dx, + double step) +{ + struct normalized_coords motion; + uint64_t time = 0; + double dx; + int i; + + printf("# gnuplot:\n"); + printf("# set xlabel \"event number\"\n"); + printf("# set ylabel \"delta motion\"\n"); + printf("# set style data lines\n"); + printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n"); + printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n"); + printf("#\n"); + + if (nevents == 0) { + if (step > 1.0) + nevents = max_dx; + else + nevents = 1.0 * max_dx/step + 0.5; + + /* Print more events than needed so we see the curve + * flattening out */ + nevents *= 1.5; + } + + dx = 0; + + for (i = 0; i < nevents; i++) { + motion.x = dx; + motion.y = 0; + time += 12; /* pretend 80Hz data */ + + filter_dispatch(filter, &motion, NULL, time); + + printf("%d %.3f %.3f\n", i, motion.x, dx); + + if (dx < max_dx) + dx += step; + } +} + +static void +print_ptraccel_sequence(struct motion_filter *filter, + int nevents, + double *deltas) +{ + struct normalized_coords motion; + uint64_t time = 0; + double *dx; + int i; + + printf("# gnuplot:\n"); + printf("# set xlabel \"event number\"\n"); + printf("# set ylabel \"delta motion\"\n"); + printf("# set style data lines\n"); + printf("# plot \"gnuplot.data\" using 1:2 title \"dx out\", \\\n"); + printf("# \"gnuplot.data\" using 1:3 title \"dx in\"\n"); + printf("#\n"); + + dx = deltas; + + for (i = 0; i < nevents; i++, dx++) { + motion.x = *dx; + motion.y = 0; + time += 12; /* pretend 80Hz data */ + + filter_dispatch(filter, &motion, NULL, time); + + printf("%d %.3f %.3f\n", i, motion.x, *dx); + } +} + +static void +print_accel_func(struct motion_filter *filter) +{ + double vel; + + printf("# gnuplot:\n"); + printf("# set xlabel \"speed\"\n"); + printf("# set ylabel \"raw accel factor\"\n"); + printf("# set style data lines\n"); + printf("# plot \"gnuplot.data\" using 1:2\n"); + for (vel = 0.0; vel < 3.0; vel += .0001) { + double result = pointer_accel_profile_linear(filter, + NULL, + vel, + 0 /* time */); + printf("%.4f\t%.4f\n", vel, result); + } +} + +static void +usage(void) +{ + printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n", program_invocation_short_name); + printf("\n" + "Options:\n" + "--mode= \n" + " motion ... print motion to accelerated motion (default)\n" + " delta ... print delta to accelerated delta\n" + " accel ... print accel factor\n" + " sequence ... print motion for custom delta sequence\n" + "--maxdx=\n ... in motion mode only. Stop increasing dx at maxdx\n" + "--steps=\n ... in motion and delta modes only. Increase dx by step each round\n" + "--speed=\n ... accel speed [-1, 1], default 0\n" + "\n" + "If extra arguments are present and mode is not given, mode defaults to 'sequence'\n" + "and the arguments are interpreted as sequence of delta x coordinates\n" + "\n" + "If stdin is a pipe, mode defaults to 'sequence' and the pipe is read \n" + "for delta coordinates\n" + "\n" + "Output best viewed with gnuplot. See output for gnuplot commands\n"); +} + +int +main(int argc, char **argv) { + struct motion_filter *filter; + double step = 0.1, + max_dx = 10; + int nevents = 0; + bool print_accel = false, + print_motion = true, + print_delta = false, + print_sequence = false; + double custom_deltas[1024]; + double speed = 0.0; + enum { + OPT_MODE = 1, + OPT_NEVENTS, + OPT_MAXDX, + OPT_STEP, + OPT_SPEED, + }; + + filter = create_pointer_accelerator_filter(pointer_accel_profile_linear); + assert(filter != NULL); + + while (1) { + int c; + int option_index = 0; + static struct option long_options[] = { + {"mode", 1, 0, OPT_MODE }, + {"nevents", 1, 0, OPT_NEVENTS }, + {"maxdx", 1, 0, OPT_MAXDX }, + {"step", 1, 0, OPT_STEP }, + {"speed", 1, 0, OPT_SPEED }, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case OPT_MODE: + if (strcmp(optarg, "accel") == 0) + print_accel = true; + else if (strcmp(optarg, "motion") == 0) + print_motion = true; + else if (strcmp(optarg, "delta") == 0) + print_delta = true; + else if (strcmp(optarg, "sequence") == 0) + print_sequence = true; + else { + usage(); + return 1; + } + break; + case OPT_NEVENTS: + nevents = atoi(optarg); + if (nevents == 0) { + usage(); + return 1; + } + break; + case OPT_MAXDX: + max_dx = strtod(optarg, NULL); + if (max_dx == 0.0) { + usage(); + return 1; + } + break; + case OPT_STEP: + step = strtod(optarg, NULL); + if (step == 0.0) { + usage(); + return 1; + } + break; + case OPT_SPEED: + speed = strtod(optarg, NULL); + break; + default: + usage(); + exit(1); + break; + } + } + + filter_set_speed(filter, speed); + + if (!isatty(STDIN_FILENO)) { + char buf[12]; + print_sequence = true; + print_motion = false; + nevents = 0; + memset(custom_deltas, 0, sizeof(custom_deltas)); + + while(fgets(buf, sizeof(buf), stdin) && nevents < 1024) { + custom_deltas[nevents++] = strtod(buf, NULL); + } + } else if (optind < argc) { + print_sequence = true; + print_motion = false; + nevents = 0; + memset(custom_deltas, 0, sizeof(custom_deltas)); + while (optind < argc) + custom_deltas[nevents++] = strtod(argv[optind++], NULL); + } + + if (print_accel) + print_accel_func(filter); + else if (print_delta) + print_ptraccel_deltas(filter, step); + else if (print_motion) + print_ptraccel_movement(filter, nevents, max_dx, step); + else if (print_sequence) + print_ptraccel_sequence(filter, nevents, custom_deltas); + + filter_destroy(filter); + + return 0; +} From e15a7a740d72582e47037c832cd1e283a8bbef62 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Apr 2015 10:23:45 +1000 Subject: [PATCH 027/165] evdev: any scroll device must have CAP_POINTER Follow-up to e2f61b8fb74a06b86abd02caa4cdfde5edc53a32. Scroll events are sent through the pointer interface, so we must set the capability. Otherwise a caller may not have the required bits set up and is a bit surprised by events coming out of an interface the device doesn't actually have (xf86-input-libinput crashes when this happens). Reported-by: Hans de Goede Signed-off-by: Peter Hutterer Tested-by: Hans de Goede Reviewed-by: Hans de Goede --- src/evdev.c | 4 +++- test/device.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index 243cd222..bcc2958b 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1700,8 +1700,10 @@ evdev_configure_device(struct evdev_device *device) /* want natural-scroll config option */ if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) || - libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) + libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) { device->scroll.natural_scrolling_enabled = true; + device->seat_caps |= EVDEV_DEVICE_POINTER; + } } if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) { diff --git a/test/device.c b/test/device.c index cf9885ad..ed3c2e02 100644 --- a/test/device.c +++ b/test/device.c @@ -979,6 +979,17 @@ START_TEST(abs_mt_device_missing_res) } END_TEST + +START_TEST(device_wheel_only) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + + ck_assert(libinput_device_has_capability(device, + LIBINPUT_DEVICE_CAP_POINTER)); +} +END_TEST + int main (int argc, char **argv) { litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); @@ -1016,5 +1027,7 @@ int main (int argc, char **argv) litest_add_no_device("device:invalid devices", abs_device_missing_res); litest_add_no_device("device:invalid devices", abs_mt_device_missing_res); + litest_add("device:wheel", device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE); + return litest_run(argc, argv); } From c830c1ea3a114daaf1f12c1fab39cb5d843791b9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Apr 2015 10:23:46 +1000 Subject: [PATCH 028/165] Don't post a events for a missing capability Log a bug instead. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/libinput.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/libinput.c b/src/libinput.c index 80144571..bcb2509d 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1065,6 +1065,35 @@ notify_removed_device(struct libinput_device *device) &removed_device_event->base); } +static inline bool +device_has_cap(struct libinput_device *device, + enum libinput_device_capability cap) +{ + const char *capability; + + if (libinput_device_has_capability(device, cap)) + return true; + + switch (cap) { + case LIBINPUT_DEVICE_CAP_POINTER: + capability = "CAP_POINTER"; + break; + case LIBINPUT_DEVICE_CAP_KEYBOARD: + capability = "CAP_KEYBOARD"; + break; + case LIBINPUT_DEVICE_CAP_TOUCH: + capability = "CAP_TOUCH"; + break; + } + + log_bug_libinput(device->seat->libinput, + "Event for missing capability %s on device \"%s\"\n", + capability, + libinput_device_get_name(device)); + + return false; +} + void keyboard_notify_key(struct libinput_device *device, uint64_t time, @@ -1074,6 +1103,9 @@ keyboard_notify_key(struct libinput_device *device, struct libinput_event_keyboard *key_event; uint32_t seat_key_count; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) + return; + key_event = zalloc(sizeof *key_event); if (!key_event) return; @@ -1100,6 +1132,9 @@ pointer_notify_motion(struct libinput_device *device, { struct libinput_event_pointer *motion_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER)) + return; + motion_event = zalloc(sizeof *motion_event); if (!motion_event) return; @@ -1122,6 +1157,9 @@ pointer_notify_motion_absolute(struct libinput_device *device, { struct libinput_event_pointer *motion_absolute_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER)) + return; + motion_absolute_event = zalloc(sizeof *motion_absolute_event); if (!motion_absolute_event) return; @@ -1145,6 +1183,9 @@ pointer_notify_button(struct libinput_device *device, struct libinput_event_pointer *button_event; int32_t seat_button_count; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER)) + return; + button_event = zalloc(sizeof *button_event); if (!button_event) return; @@ -1175,6 +1216,9 @@ pointer_notify_axis(struct libinput_device *device, { struct libinput_event_pointer *axis_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER)) + return; + axis_event = zalloc(sizeof *axis_event); if (!axis_event) return; @@ -1201,6 +1245,9 @@ touch_notify_touch_down(struct libinput_device *device, { struct libinput_event_touch *touch_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH)) + return; + touch_event = zalloc(sizeof *touch_event); if (!touch_event) return; @@ -1226,6 +1273,9 @@ touch_notify_touch_motion(struct libinput_device *device, { struct libinput_event_touch *touch_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH)) + return; + touch_event = zalloc(sizeof *touch_event); if (!touch_event) return; @@ -1250,6 +1300,9 @@ touch_notify_touch_up(struct libinput_device *device, { struct libinput_event_touch *touch_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH)) + return; + touch_event = zalloc(sizeof *touch_event); if (!touch_event) return; @@ -1271,6 +1324,9 @@ touch_notify_frame(struct libinput_device *device, { struct libinput_event_touch *touch_event; + if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_TOUCH)) + return; + touch_event = zalloc(sizeof *touch_event); if (!touch_event) return; From bd6fd8bb9292ece29fcbd574331dc93cf63263e5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Apr 2015 10:23:47 +1000 Subject: [PATCH 029/165] test: add a scrollwheel-only device This currently requires a specific udev rule to tag the device, once the matching bits are upstream we can drop this. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 1 + test/litest-wheel-only.c | 68 ++++++++++++++++++++++++++++++++++++++++ test/litest.c | 2 ++ test/litest.h | 1 + 4 files changed, 72 insertions(+) create mode 100644 test/litest-wheel-only.c diff --git a/test/Makefile.am b/test/Makefile.am index 8fdb4cac..fdfb84cf 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -30,6 +30,7 @@ liblitest_la_SOURCES = \ litest-trackpoint.c \ litest-wacom-touch.c \ litest-wacom-intuos-finger.c \ + litest-wheel-only.c \ litest-xen-virtual-pointer.c \ litest-vmware-virtual-usb-mouse.c \ litest.c diff --git a/test/litest-wheel-only.c b/test/litest-wheel-only.c new file mode 100644 index 00000000..e087dd26 --- /dev/null +++ b/test/litest-wheel-only.c @@ -0,0 +1,68 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "litest.h" +#include "litest-int.h" + +static void litest_wheel_only_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_WHEEL_ONLY); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x1, + .product = 0x2, +}; + +static int events[] = { + EV_REL, REL_WHEEL, + -1 , -1, +}; + +static const char udev_rule[] = +"ACTION==\"remove\", GOTO=\"wheel_only_end\"\n" +"KERNEL!=\"event*\", GOTO=\"wheel_only_end\"\n" +"\n" +"ATTRS{name}==\"litest wheel only device*\",\\\n" +" ENV{ID_INPUT_KEY}=\"1\"\n" +"\n" +"LABEL=\"wheel_only_end\""; + +struct litest_test_device litest_wheel_only_device = { + .type = LITEST_WHEEL_ONLY, + .features = LITEST_WHEEL, + .shortname = "wheel only", + .setup = litest_wheel_only_setup, + .interface = NULL, + + .name = "wheel only device", + .id = &input_id, + .absinfo = NULL, + .events = events, + .udev_rule = udev_rule, +}; diff --git a/test/litest.c b/test/litest.c index b1918e21..5c4f84dd 100644 --- a/test/litest.c +++ b/test/litest.c @@ -99,6 +99,7 @@ extern struct litest_test_device litest_synaptics_carbon3rd_device; extern struct litest_test_device litest_protocol_a_screen; extern struct litest_test_device litest_wacom_finger_device; extern struct litest_test_device litest_keyboard_blackwidow_device; +extern struct litest_test_device litest_wheel_only_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -119,6 +120,7 @@ struct litest_test_device* devices[] = { &litest_protocol_a_screen, &litest_wacom_finger_device, &litest_keyboard_blackwidow_device, + &litest_wheel_only_device, NULL, }; diff --git a/test/litest.h b/test/litest.h index d2ed3d03..84567be3 100644 --- a/test/litest.h +++ b/test/litest.h @@ -54,6 +54,7 @@ enum litest_device_type { LITEST_PROTOCOL_A_SCREEN = -18, LITEST_WACOM_FINGER = -19, LITEST_KEYBOARD_BLACKWIDOW = -20, + LITEST_WHEEL_ONLY = -21, }; enum litest_device_feature { From 586ac7c4300dfec114a7f8a6f4288b2a2fdef64f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 15 Apr 2015 10:21:47 +1000 Subject: [PATCH 030/165] Return INVALID before UNSUPPORTED for click methods All other config options already follow this behavior. Signed-off-by: Peter Hutterer --- src/libinput.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libinput.c b/src/libinput.c index bcb2509d..980b44a6 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1929,9 +1929,6 @@ LIBINPUT_EXPORT enum libinput_config_status libinput_device_config_click_set_method(struct libinput_device *device, enum libinput_config_click_method method) { - if ((libinput_device_config_click_get_methods(device) & method) != method) - return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; - /* Check method is a single valid method */ switch (method) { case LIBINPUT_CONFIG_CLICK_METHOD_NONE: @@ -1942,6 +1939,9 @@ libinput_device_config_click_set_method(struct libinput_device *device, return LIBINPUT_CONFIG_STATUS_INVALID; } + if ((libinput_device_config_click_get_methods(device) & method) != method) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + if (device->config.click_method) return device->config.click_method->set_method(device, method); else /* method must be _NONE to get here */ From 6f1c8cda85810b6655c09437e45e73a096ece493 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 15 Apr 2015 10:09:34 +1000 Subject: [PATCH 031/165] evdev: add a event-debugging log statement (ifdef'd out) Signed-off-by: Peter Hutterer --- src/evdev.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index bcc2958b..90f13a33 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1137,6 +1137,19 @@ evdev_process_event(struct evdev_device *device, struct input_event *e) struct evdev_dispatch *dispatch = device->dispatch; uint64_t time = e->time.tv_sec * 1000ULL + e->time.tv_usec / 1000; +#if 0 + if (libevdev_event_is_code(e, EV_SYN, SYN_REPORT)) + log_debug(device->base.seat->libinput, + "-------------- EV_SYN ------------\n"); + else + log_debug(device->base.seat->libinput, + "%-7s %-16s %-20s %4d\n", + evdev_device_get_sysname(device), + libevdev_event_type_get_name(e->type), + libevdev_event_code_get_name(e->type, e->code), + e->value); +#endif + dispatch->interface->process(dispatch, device, e, time); } From a5c7de04c42fe8360e519fbb2d9328d00a9ed133 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 12:26:01 +1000 Subject: [PATCH 032/165] evdev: merge two conditions into one with a sub condition Signed-off-by: Peter Hutterer --- src/evdev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 90f13a33..5b4b2b6b 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -151,13 +151,13 @@ evdev_pointer_notify_button(struct evdev_device *device, (state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) { pointer_notify_button(&device->base, time, button, state); - if (state == LIBINPUT_BUTTON_STATE_RELEASED && - device->left_handed.change_to_enabled) - device->left_handed.change_to_enabled(device); + if (state == LIBINPUT_BUTTON_STATE_RELEASED) { + if (device->left_handed.change_to_enabled) + device->left_handed.change_to_enabled(device); - if (state == LIBINPUT_BUTTON_STATE_RELEASED && - device->scroll.change_scroll_method) - device->scroll.change_scroll_method(device); + if (device->scroll.change_scroll_method) + device->scroll.change_scroll_method(device); + } } } From 120199514b9deeb540c59b5604cd5f9c5d0c124b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 15 Apr 2015 15:33:41 +1000 Subject: [PATCH 033/165] touchpad: extend two debug messages Makes it quicker to know where it's coming from. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index b99249bf..69c9669f 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -147,7 +147,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: case TAP_EVENT_MOTION: log_bug_libinput(libinput, - "invalid event, no fingers are down\n"); + "invalid tap event, no fingers are down\n"); break; case TAP_EVENT_TIMEOUT: break; @@ -218,7 +218,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, case TAP_EVENT_MOTION: case TAP_EVENT_RELEASE: log_bug_libinput(libinput, - "invalid event when fingers are up\n"); + "invalid tap event when fingers are up\n"); break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP; From c74d07bee936d41ab887ad651f58ddaae8bc9da6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 15 Apr 2015 11:44:54 +1000 Subject: [PATCH 034/165] tools: add a tool to list local devices and the default configurations xinput or an equivalent isn't available under wayland, but the majority of use-cases of "why doesn't my device work" or "why does feature X not work" should be covered by simply listing the local devices and their config options. Example output: Device: SynPS/2 Synaptics TouchPad Kernel: /dev/input/event4 Group: 9 Seat: seat0, default Size: 97.33x62.40mm Capabilities: pointer Tap-to-click: disabled Left-handed: disabled Nat.scrolling: disabled Calibration: n/a Scroll methods: *two-finger Click methods: *button-areas clickfinger Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/.gitignore | 1 + tools/Makefile.am | 6 + tools/libinput-list-devices.c | 282 ++++++++++++++++++++++++++++++++ tools/libinput-list-devices.man | 37 +++++ 4 files changed, 326 insertions(+) create mode 100644 tools/libinput-list-devices.c create mode 100644 tools/libinput-list-devices.man diff --git a/tools/.gitignore b/tools/.gitignore index 6d530e67..e58dba95 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,3 +1,4 @@ event-debug event-gui ptraccel-debug +libinput-list-devices diff --git a/tools/Makefile.am b/tools/Makefile.am index 34d5ab07..b8cc218b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,5 @@ noinst_PROGRAMS = event-debug ptraccel-debug +bin_PROGRAMS = libinput-list-devices noinst_LTLIBRARIES = libshared.la AM_CPPFLAGS = -I$(top_srcdir)/include \ @@ -18,6 +19,11 @@ ptraccel_debug_SOURCES = ptraccel-debug.c ptraccel_debug_LDADD = ../src/libfilter.la ptraccel_debug_LDFLAGS = -no-install +libinput_list_devices_SOURCES = libinput-list-devices.c +libinput_list_devices_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) +libinput_list_devices_CFLAGS = $(LIBUDEV_CFLAGS) +man1_MANS = libinput-list-devices.man + if BUILD_EVENTGUI noinst_PROGRAMS += event-gui diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c new file mode 100644 index 00000000..24c7c532 --- /dev/null +++ b/tools/libinput-list-devices.c @@ -0,0 +1,282 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared.h" + +static int +open_restricted(const char *path, int flags, void *user_data) +{ + int fd = open(path, flags); + if (fd < 0) + fprintf(stderr, "Failed to open %s (%s)\n", + path, strerror(errno)); + return fd < 0 ? -errno : fd; +} + +static void +close_restricted(int fd, void *user_data) +{ + close(fd); +} + +static const struct libinput_interface interface = { + .open_restricted = open_restricted, + .close_restricted = close_restricted, +}; + +static inline const char* +bool_to_str(bool b) +{ + if (b) + return "yes"; + else + return "no"; +} + +static const char * +tap_default(struct libinput_device *device) +{ + if (!libinput_device_config_tap_get_finger_count(device)) + return "n/a"; + + if (libinput_device_config_tap_get_default_enabled(device)) + return "enabled"; + else + return "disabled"; +} + +static const char* +left_handed_default(struct libinput_device *device) +{ + if (!libinput_device_config_left_handed_is_available(device)) + return "n/a"; + + if (libinput_device_config_left_handed_get_default(device)) + return "enabled"; + else + return "disabled"; +} + +static const char * +nat_scroll_default(struct libinput_device *device) +{ + if (!libinput_device_config_scroll_has_natural_scroll(device)) + return "n/a"; + + if (libinput_device_config_scroll_get_default_natural_scroll_enabled(device)) + return "enabled"; + else + return "disabled"; +} + +static char * +calibration_default(struct libinput_device *device) +{ + char *str; + float calibration[6]; + + if (!libinput_device_config_calibration_has_matrix(device)) { + asprintf(&str, "n/a"); + return str; + } + + if (libinput_device_config_calibration_get_default_matrix(device, + calibration) == 0) { + asprintf(&str, "identity matrix"); + return str; + } + + asprintf(&str, + "%.2f %.2f %.2f %.2f %.2f %.2f", + calibration[0], + calibration[1], + calibration[2], + calibration[3], + calibration[4], + calibration[5]); + return str; +} + +static char * +scroll_defaults(struct libinput_device *device) +{ + uint32_t scroll_methods; + char *str; + enum libinput_config_scroll_method method; + + scroll_methods = libinput_device_config_scroll_get_methods(device); + if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL) { + asprintf(&str, "none"); + return str; + } + + method = libinput_device_config_scroll_get_default_method(device); + + asprintf(&str, + "%s%s%s%s%s%s", + (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "", + (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "", + (method == LIBINPUT_CONFIG_SCROLL_EDGE) ? "*" : "", + (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "edge " : "", + (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "*" : "", + (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "button" : ""); + return str; +} + +static char* +click_defaults(struct libinput_device *device) +{ + uint32_t click_methods; + char *str; + enum libinput_config_click_method method; + + click_methods = libinput_device_config_click_get_methods(device); + if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE) { + asprintf(&str, "none"); + return str; + } + + method = libinput_device_config_click_get_default_method(device); + asprintf(&str, + "%s%s%s%s", + (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "", + (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "", + (method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "*" : "", + (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "clickfinger " : ""); + return str; +} + +static void +print_device_notify(struct libinput_event *ev) +{ + struct libinput_device *dev = libinput_event_get_device(ev); + struct libinput_seat *seat = libinput_device_get_seat(dev); + struct libinput_device_group *group; + double w, h; + static int next_group_id = 0; + intptr_t group_id; + const char *devnode; + char *str; + + group = libinput_device_get_device_group(dev); + group_id = (intptr_t)libinput_device_group_get_user_data(group); + if (!group_id) { + group_id = ++next_group_id; + libinput_device_group_set_user_data(group, (void*)group_id); + } + + devnode = udev_device_get_devnode( + libinput_device_get_udev_device(dev)); + + printf("Device: %s\n" + "Kernel: %s\n" + "Group: %d\n" + "Seat: %s, %s\n", + libinput_device_get_name(dev), + devnode, + (int)group_id, + libinput_seat_get_physical_name(seat), + libinput_seat_get_logical_name(seat)); + + if (libinput_device_get_size(dev, &w, &h) == 0) + printf("Size: %.2fx%.2fmm\n", w, h); + printf("Capabilities: "); + if (libinput_device_has_capability(dev, + LIBINPUT_DEVICE_CAP_KEYBOARD)) + printf("keyboard "); + if (libinput_device_has_capability(dev, + LIBINPUT_DEVICE_CAP_POINTER)) + printf("pointer "); + if (libinput_device_has_capability(dev, + LIBINPUT_DEVICE_CAP_TOUCH)) + printf("touch"); + printf("\n"); + + printf("Tap-to-click: %s\n", tap_default(dev)); + printf("Left-handed: %s\n", left_handed_default(dev)); + printf("Nat.scrolling: %s\n", nat_scroll_default(dev)); + str = calibration_default(dev); + printf("Calibration: %s\n", str); + free(str); + + str = scroll_defaults(dev); + printf("Scroll methods: %s\n", str); + free(str); + + str = click_defaults(dev); + printf("Click methods: %s\n", str); + free(str); + + printf("\n"); +} + +int +main(int argc, char **argv) +{ + struct libinput *li; + struct tools_options options; + struct libinput_event *ev; + + if (argc > 1) { + printf("Usage: %s [--help]\n" + "\n" + "This tool creates a libinput context on the default seat \"seat0\"\n" + "and lists all devices recognized by libinput and the configuration options.\n" + "Where multiple options are possible, the default is prefixed with \"*\".\n" + "\n" + "This tool requires access to the /dev/input/eventX nodes.\n", + program_invocation_short_name); + + return 1; + } + + tools_init_options(&options); + + li = tools_open_backend(&options, NULL, &interface); + if (!li) + return 1; + + libinput_dispatch(li); + while ((ev = libinput_get_event(li))) { + + if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) + print_device_notify(ev); + + libinput_event_destroy(ev); + libinput_dispatch(li); + } + + libinput_unref(li); + + return 0; +} diff --git a/tools/libinput-list-devices.man b/tools/libinput-list-devices.man new file mode 100644 index 00000000..afa90905 --- /dev/null +++ b/tools/libinput-list-devices.man @@ -0,0 +1,37 @@ +.TH LIBINPUT-LIST_DEVICES "1" +.SH NAME +libinput-list-devices \- list local devices as recognized by libinput +.SH SYNOPSIS +.B libinput-list-devices [--help] +.SH DESCRIPTION +.PP +The +.I libinput-list-devices +tool creates a libinput context on the default seat "seat0" and lists all +devices regonized by libinput. Each device shows available configurations +the respective default configuration setting. +.PP +For configuration options that allow multiple different settings (e.g. +scrolling), all available settings are listed. The default setting is +prefixed by an asterisk (*). +.PP +This tool usually needs to be run as root to have access to the +/dev/input/eventX nodes. +.SH OPTIONS +.TP 8 +.B --help +Print help +.SH NOTES +.PP +Some specific feature may still be available on a device even when +no configuration is exposed, a lack of a configuration option does not +necessarily mean that this feature does not work. +.PP +A device may be recognized by libinput but not handled by the X.Org libinput +driver or the Wayland compositor. +.PP +An xorg.conf(5) configuration entry or Wayland compositor setting may have +changed configurations on a device. The +.I libinput-list-devices +tool only shows the device's default configuration, not the current +configuration. From 7552cd04f7a50fb674ec826933dbdb7f804d7447 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 17:04:45 +1000 Subject: [PATCH 035/165] touchpad: only pair internal trackpoint devices with internal touchpads Internal touchpads with trackpoints are either BUS_I8042 or BUS_I2C, but not BUS_USB. Lenovo sells external keyboards with a trackpoint built-in, make sure we don't pair that trackpoint with the internal touchpad. And likewise, the internal trackpoint should not be paired with e.g. a wacom touch device. Lenovo had one external device that has a trackpoint and a touchpad on an external keyboard. That device won't be covered with this patch, if we have a user we can re-consider. https://bugs.freedesktop.org/show_bug.cgi?id=89935 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 68070c2e..16846e9d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -825,9 +825,16 @@ tp_device_added(struct evdev_device *device, struct evdev_device *added_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; + unsigned int bus_tp = libevdev_get_id_bustype(device->evdev), + bus_trp = libevdev_get_id_bustype(added_device->evdev); + bool tp_is_internal, trp_is_internal; + + tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH; + trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH; if (tp->buttons.trackpoint == NULL && - (added_device->tags & EVDEV_TAG_TRACKPOINT)) { + (added_device->tags & EVDEV_TAG_TRACKPOINT) && + tp_is_internal && trp_is_internal) { /* Don't send any pending releases to the new trackpoint */ tp->buttons.active_is_topbutton = false; tp->buttons.trackpoint = added_device; From 39f11253471500cd380c8977b8cbe0683abe6b1d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 15 Apr 2015 15:21:08 +1000 Subject: [PATCH 036/165] touchpad: don't allow taps in the top half of the palm exclusion zone. Touches in the exclusion zone are ignored for palm detection and don't move the cursor. Tapping however triggers before we know whether something is a palm or not, so we get erroneous button clickst. If a tap happens in the top half of the touchpad, within the palm exclusion zones, ignore it for tap purposes. To avoid further complicating the state machine simply pretend there was a movement > threshold on that finger. This advances the tap state machine properly that no button events are sent for this finger. https://bugs.freedesktop.org/show_bug.cgi?id=89625 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/palm-detection.dox | 9 ++ doc/svg/palm-detection.svg | 213 ++++++++++++++++++++++++++---------- src/evdev-mt-touchpad-tap.c | 8 ++ src/evdev-mt-touchpad.c | 24 +++- src/evdev-mt-touchpad.h | 4 + test/touchpad.c | 44 ++++++++ 6 files changed, 244 insertions(+), 58 deletions(-) diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index 4e839e62..a5b578ba 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -23,6 +23,10 @@ screen, it is common for a finger to start inside an exclusion zone and move rapidly across the touchpad. libinput detects such movements and avoids palm detection on such touch sequences. +Each exclusion zone is divided into a top part and a bottom part. A touch +starting in the top part of the exclusion zone does not trigger a +tap (see @ref tapping). + In the diagram below, the exclusion zones are painted red. Touch 'A' starts inside the exclusion zone and moves almost vertically. It is considered a palm and ignored for cursor movement, @@ -31,6 +35,11 @@ despite moving out of the exclusion zone. Touch 'B' starts inside the exclusion zone but moves horizontally out of the zone. It is considered a valid touch and controls the cursor. +Touch 'C' occurs in the top part of the exclusion zone. Despite being a +tapping motion, it does not generate an emulated button event. Touch 'D' +likewise occurs within the exclusion zone but in the bottom half. libinput +will generate a button event for this touch. + @image html palm-detection.svg @section trackpoint-disabling Palm detection during trackpoint use diff --git a/doc/svg/palm-detection.svg b/doc/svg/palm-detection.svg index 9fb6077d..c3e45f44 100644 --- a/doc/svg/palm-detection.svg +++ b/doc/svg/palm-detection.svg @@ -2,14 +2,66 @@ + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="palm-detection.svg"> + + + + image/svg+xml + + + + + + + + - - - - - - A - B - - + + + + + + A + + B + + C + D + + diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 69c9669f..49fabb50 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -571,6 +571,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.state = TAP_TOUCH_STATE_TOUCH; t->tap.initial = t->point; tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); + + /* If we think this is a palm, pretend there's a + * motion event which will prevent tap clicks + * without requiring extra states in the FSM. + */ + if (tp_palm_tap_is_palm(tp, t)) + tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); + } else if (t->state == TOUCH_END) { tp->tap.tap_finger_count--; tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time); diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 16846e9d..4e5a5580 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -458,6 +458,24 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) tp_edge_scroll_touch_active(tp, t); } +bool +tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t) +{ + if (t->state != TOUCH_BEGIN) + return false; + + if (t->point.x > tp->palm.left_edge && + t->point.x < tp->palm.right_edge) + return false; + + /* We're inside the left/right palm edge and in the northern half of + * the touchpad - this tap is a palm */ + if (t->point.y < tp->palm.vert_center) + return true; + + return false; +} + static void tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) { @@ -1098,13 +1116,16 @@ static int tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device) { - int width; + int width, height; tp->palm.right_edge = INT_MAX; tp->palm.left_edge = INT_MIN; + tp->palm.vert_center = INT_MIN; width = abs(device->abs.absinfo_x->maximum - device->abs.absinfo_x->minimum); + height = abs(device->abs.absinfo_y->maximum - + device->abs.absinfo_y->minimum); /* Apple touchpads are always big enough to warrant palm detection */ if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) { @@ -1121,6 +1142,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, /* palm edges are 5% of the width on each side */ tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05; tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05; + tp->palm.vert_center = device->abs.absinfo_y->minimum + height/2; return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 6ab09817..7b7600c3 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -261,6 +261,7 @@ struct tp_dispatch { struct { int32_t right_edge; /* in device coordinates */ int32_t left_edge; /* in device coordinates */ + int32_t vert_center; /* in device coordinates */ } palm; struct { @@ -387,4 +388,7 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time); void tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time); +bool +tp_palm_tap_is_palm(struct tp_dispatch *tp, struct tp_touch *t); + #endif diff --git a/test/touchpad.c b/test/touchpad.c index b06e00dc..cd86f049 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2626,6 +2626,49 @@ START_TEST(touchpad_palm_detect_no_palm_moving_into_edges) } END_TEST +START_TEST(touchpad_palm_detect_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!touchpad_has_palm_detect_size(dev)) + return; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 95, 5); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 5); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 5, 90); + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 95, 90); + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_left_handed) { struct litest_device *dev = litest_current_device(); @@ -3681,6 +3724,7 @@ int main(int argc, char **argv) { litest_add("touchpad:palm", touchpad_palm_detect_palm_becomes_pointer, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_palm_stays_palm, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_no_palm_moving_into_edges, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:palm", touchpad_palm_detect_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:left-handed", touchpad_left_handed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:left-handed", touchpad_left_handed_clickpad, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); From 26062e84693341d5a6b7b33af91a50a9cd37dfe2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 16 Apr 2015 16:13:01 +1000 Subject: [PATCH 037/165] touchpad: reduce palm detection threshold to 70mm https://bugzilla.redhat.com/show_bug.cgi?id=1209753 lists a touchpad 76mm wide that suffers from palm touches Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 2 +- test/touchpad.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 4e5a5580..452cb851 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1135,7 +1135,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, /* Enable palm detection on touchpads >= 80 mm. Anything smaller probably won't need it, until we find out it does */ - if (width/device->abs.absinfo_x->resolution < 80) + if (width/device->abs.absinfo_x->resolution < 70) return 0; } diff --git a/test/touchpad.c b/test/touchpad.c index cd86f049..4e425f58 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2469,7 +2469,7 @@ touchpad_has_palm_detect_size(struct litest_device *dev) rc = libinput_device_get_size(dev->libinput_device, &width, &height); - return rc == 0 && width >= 80; + return rc == 0 && width >= 70; } START_TEST(touchpad_palm_detect_at_edge) From da595b89524cffc388ceecd7103a800d4086e58f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 15:18:19 +1000 Subject: [PATCH 038/165] toucphad: fix a comment for the new min touchpad palm detection size Missing from 26062e84693341d5a6b7b33af91a50a9cd37dfe2 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 452cb851..cccf033d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1133,7 +1133,7 @@ tp_init_palmdetect(struct tp_dispatch *tp, if (device->abs.absinfo_x->resolution == 1) return 0; - /* Enable palm detection on touchpads >= 80 mm. Anything smaller + /* Enable palm detection on touchpads >= 70 mm. Anything smaller probably won't need it, until we find out it does */ if (width/device->abs.absinfo_x->resolution < 70) return 0; From 3c500b597c4eeb1ab4b279387f99da4acecc8ba1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Apr 2015 14:47:26 +1000 Subject: [PATCH 039/165] evdev: add evdev_pointer_notify_physical_button No functional changes at this point, this merely splits up any physical buttons (i.e. that represent buttons that exist on that device) vs. other buttons that are emulated in some way or another. This is in preparation for the addition of middle button emulation. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 8 ++++---- src/evdev.c | 17 +++++++++++++---- src/evdev.h | 5 +++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 18c32fda..56a054ce 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -734,10 +734,10 @@ tp_post_physical_buttons(struct tp_dispatch *tp, uint64_t time) state = LIBINPUT_BUTTON_STATE_RELEASED; b = evdev_to_left_handed(tp->device, button); - evdev_pointer_notify_button(tp->device, - time, - b, - state); + evdev_pointer_notify_physical_button(tp->device, + time, + b, + state); } button++; diff --git a/src/evdev.c b/src/evdev.c index 5b4b2b6b..6f87484d 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -137,6 +137,15 @@ evdev_keyboard_notify_key(struct evdev_device *device, keyboard_notify_key(&device->base, time, key, state); } +void +evdev_pointer_notify_physical_button(struct evdev_device *device, + uint32_t time, + int button, + enum libinput_button_state state) +{ + evdev_pointer_notify_button(device, time, button, state); +} + void evdev_pointer_notify_button(struct evdev_device *device, uint32_t time, @@ -430,10 +439,10 @@ evdev_button_scroll_button(struct evdev_device *device, } else { /* If the button is released quickly enough emit the * button press/release events. */ - evdev_pointer_notify_button(device, time, + evdev_pointer_notify_physical_button(device, time, device->scroll.button, LIBINPUT_BUTTON_STATE_PRESSED); - evdev_pointer_notify_button(device, time, + evdev_pointer_notify_physical_button(device, time, device->scroll.button, LIBINPUT_BUTTON_STATE_RELEASED); } @@ -505,7 +514,7 @@ evdev_process_key(struct evdev_device *device, evdev_button_scroll_button(device, time, e->value); break; } - evdev_pointer_notify_button( + evdev_pointer_notify_physical_button( device, time, evdev_to_left_handed(device, e->code), @@ -2208,7 +2217,7 @@ release_pressed_keys(struct evdev_device *device) LIBINPUT_KEY_STATE_RELEASED); break; case EVDEV_KEY_TYPE_BUTTON: - evdev_pointer_notify_button( + evdev_pointer_notify_physical_button( device, time, evdev_to_left_handed(device, code), diff --git a/src/evdev.h b/src/evdev.h index af09baa5..f2ce9bcd 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -306,6 +306,11 @@ evdev_pointer_notify_button(struct evdev_device *device, uint32_t time, int button, enum libinput_button_state state); +void +evdev_pointer_notify_physical_button(struct evdev_device *device, + uint32_t time, + int button, + enum libinput_button_state state); void evdev_init_natural_scroll(struct evdev_device *device); From a6c4115692a46243f911aa7588bb4dbb25ef9faa Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 12:08:33 +1000 Subject: [PATCH 040/165] Add middle mouse button emulation config options Adds the following quartett of functions to enable/disable middle mouse button emulation on a device: libinput_device_config_middle_emulation_is_available() libinput_device_config_middle_emulation_set_enabled() libinput_device_config_middle_emulation_get_enabled() libinput_device_config_middle_emulation_get_default_enabled() This patch only adds the config framework, it is not hooked up to anything yet. Note: like other features this is merely the config option, some devices will provide middle button emulation without exposing it as configuration. i.e. the return value of libinput_device_config_middle_emulation_is_available() only tells you whether you can _configure_ middle button emulation. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/libinput-private.h | 12 ++++ src/libinput.c | 49 ++++++++++++++++ src/libinput.h | 124 +++++++++++++++++++++++++++++++++++++++++ src/libinput.sym | 8 +++ tools/shared.c | 16 ++++++ tools/shared.h | 1 + 6 files changed, 210 insertions(+) diff --git a/src/libinput-private.h b/src/libinput-private.h index 36b3b40f..cec529c8 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -178,6 +178,17 @@ struct libinput_device_config_click_method { enum libinput_config_click_method (*get_default_method)(struct libinput_device *device); }; +struct libinput_device_config_middle_emulation { + int (*available)(struct libinput_device *device); + enum libinput_config_status (*set)( + struct libinput_device *device, + enum libinput_config_middle_emulation_state); + enum libinput_config_middle_emulation_state (*get)( + struct libinput_device *device); + enum libinput_config_middle_emulation_state (*get_default)( + struct libinput_device *device); +}; + struct libinput_device_config { struct libinput_device_config_tap *tap; struct libinput_device_config_calibration *calibration; @@ -187,6 +198,7 @@ struct libinput_device_config { struct libinput_device_config_left_handed *left_handed; struct libinput_device_config_scroll_method *scroll_method; struct libinput_device_config_click_method *click_method; + struct libinput_device_config_middle_emulation *middle_emulation; }; struct libinput_device_group { diff --git a/src/libinput.c b/src/libinput.c index 980b44a6..7dcf2965 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1966,6 +1966,55 @@ libinput_device_config_click_get_default_method(struct libinput_device *device) return LIBINPUT_CONFIG_CLICK_METHOD_NONE; } +LIBINPUT_EXPORT int +libinput_device_config_middle_emulation_is_available( + struct libinput_device *device) +{ + if (device->config.middle_emulation) + return device->config.middle_emulation->available(device); + else + return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; +} + +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_middle_emulation_set_enabled( + struct libinput_device *device, + enum libinput_config_middle_emulation_state enable) +{ + switch (enable) { + case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + break; + default: + return LIBINPUT_CONFIG_STATUS_INVALID; + } + + if (!libinput_device_config_middle_emulation_is_available(device)) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + + return device->config.middle_emulation->set(device, enable); +} + +LIBINPUT_EXPORT enum libinput_config_middle_emulation_state +libinput_device_config_middle_emulation_get_enabled( + struct libinput_device *device) +{ + if (!libinput_device_config_middle_emulation_is_available(device)) + return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + + return device->config.middle_emulation->get(device); +} + +LIBINPUT_EXPORT enum libinput_config_middle_emulation_state +libinput_device_config_middle_emulation_get_default_enabled( + struct libinput_device *device) +{ + if (!libinput_device_config_middle_emulation_is_available(device)) + return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + + return device->config.middle_emulation->get_default(device); +} + LIBINPUT_EXPORT uint32_t libinput_device_config_scroll_get_methods(struct libinput_device *device) { diff --git a/src/libinput.h b/src/libinput.h index 356c1bf0..b4b373e8 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -2439,6 +2439,130 @@ libinput_device_config_click_get_method(struct libinput_device *device); enum libinput_config_click_method libinput_device_config_click_get_default_method(struct libinput_device *device); +/** + * @ingroup config + */ +enum libinput_config_middle_emulation_state { + /** + * Middle mouse button emulation is to be disabled, or + * is currently disabled. + */ + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED, + /** + * Middle mouse button emulation is to be enabled, or + * is currently enabled. + */ + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED, +}; + +/** + * @ingroup config + * + * Check if middle mouse button emulation configuration is available on this + * device. See libinput_device_config_middle_emulation_set_enabled() for + * details. + * + * @note Some devices provide middle mouse button emulation but do not allow + * enabling/disabling that emulation. These devices return zero in + * libinput_device_config_middle_emulation_is_available(). + * + * @param device The device to query + * + * @return Non-zero if middle mouse button emulation is available and can be + * configured, zero otherwise. + * + * @see libinput_device_config_middle_emulation_set_enabled + * @see libinput_device_config_middle_emulation_get_enabled + * @see libinput_device_config_middle_emulation_get_default_enabled + */ +int +libinput_device_config_middle_emulation_is_available( + struct libinput_device *device); + +/** + * @ingroup config + * + * Enable or disable middle button emulation on this device. When enabled, a + * simultaneous press of the left and right button generates a middle mouse + * button event. Releasing the buttons generates a middle mouse button + * release, the left and right button events are discarded otherwise. + * + * The middle button release event may be generated when either button is + * released, or when both buttons have been released. The exact behavior is + * device-dependent. + * + * The middle button emulation behavior when combined with other device + * buttons, including a physical middle button is device-dependent. + * + * @note Some devices provide middle mouse button emulation but do not allow + * enabling/disabling that emulation. + * + * @param device The device to configure + * @param enable @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED to + * disable, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED To enable + * middle button emulation. + * + * @return A config status code. Disabling middle button emulation on a + * device that does not support middle button emulation always succeeds. + * + * @see libinput_device_config_middle_emulation_is_available + * @see libinput_device_config_middle_emulation_get_enabled + * @see libinput_device_config_middle_emulation_get_default_enabled + */ +enum libinput_config_status +libinput_device_config_middle_emulation_set_enabled( + struct libinput_device *device, + enum libinput_config_middle_emulation_state enable); + +/** + * @ingroup config + * + * Check if configurable middle button emulation is enabled on this device. + * If the device does not have configurable middle button emulation, this + * function returns @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED. + * + * @note Some devices provide middle mouse button emulation but do not allow + * enabling/disabling that emulation. These devices always return @ref + * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED. + * + * @param device The device to configure + * @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED if disabled + * or not available/configurable, @ref + * LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED If enabled. + * + * @see libinput_device_config_middle_emulation_is_available + * @see libinput_device_config_middle_emulation_set_enabled + * @see libinput_device_config_middle_emulation_get_default_enabled + */ +enum libinput_config_middle_emulation_state +libinput_device_config_middle_emulation_get_enabled( + struct libinput_device *device); + +/** + * @ingroup config + * + * Check if configurable middle button emulation is enabled by default on + * this device. If the device does not have configurable middle button + * emulation, this function returns @ref + * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED. + * + * @note Some devices provide middle mouse button emulation but do not allow + * enabling/disabling that emulation. These devices always return @ref + * LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED. + * + * @param device The device to configure + * @return @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED If disabled + * or not available, @ref LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED if + * enabled. + * + * @see libinput_device_config_middle_emulation_is_available + * @see libinput_device_config_middle_emulation_set_enabled + * @see libinput_device_config_middle_emulation_get_enabled + */ +enum libinput_config_middle_emulation_state +libinput_device_config_middle_emulation_get_default_enabled( + struct libinput_device *device); + /** * @ingroup config * diff --git a/src/libinput.sym b/src/libinput.sym index f9da9054..3c47ee5c 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -127,3 +127,11 @@ global: local: *; }; + +LIBINPUT_0.14.0 { +global: + libinput_device_config_middle_emulation_is_available; + libinput_device_config_middle_emulation_set_enabled; + libinput_device_config_middle_emulation_get_enabled; + libinput_device_config_middle_emulation_get_default_enabled; +} LIBINPUT_0.12.0; diff --git a/tools/shared.c b/tools/shared.c index b1690494..d8d23a7c 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -43,6 +43,8 @@ enum options { OPT_NATURAL_SCROLL_DISABLE, OPT_LEFT_HANDED_ENABLE, OPT_LEFT_HANDED_DISABLE, + OPT_MIDDLEBUTTON_ENABLE, + OPT_MIDDLEBUTTON_DISABLE, OPT_CLICK_METHOD, OPT_SPEED, }; @@ -71,6 +73,8 @@ tools_usage() "--disable-natural-scrolling.... enable/disable natural scrolling\n" "--enable-left-handed\n" "--disable-left-handed.... enable/disable left-handed button configuration\n" + "--enable-middlebutton\n" + "--disable-middlebutton.... enable/disable middle button emulation\n" "--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n" "--set-speed=.... set pointer acceleration speed\n" "\n" @@ -90,6 +94,7 @@ tools_init_options(struct tools_options *options) options->tapping = -1; options->natural_scroll = -1; options->left_handed = -1; + options->middlebutton = -1; options->click_method = -1; options->backend = BACKEND_UDEV; options->seat = "seat0"; @@ -113,6 +118,8 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) { "disable-natural-scrolling", 0, 0, OPT_NATURAL_SCROLL_DISABLE }, { "enable-left-handed", 0, 0, OPT_LEFT_HANDED_ENABLE }, { "disable-left-handed", 0, 0, OPT_LEFT_HANDED_DISABLE }, + { "enable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_ENABLE }, + { "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_DISABLE }, { "set-click-method", 1, 0, OPT_CLICK_METHOD }, { "speed", 1, 0, OPT_SPEED }, { 0, 0, 0, 0} @@ -161,6 +168,12 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) case OPT_LEFT_HANDED_DISABLE: options->left_handed = 0; break; + case OPT_MIDDLEBUTTON_ENABLE: + options->middlebutton = 1; + break; + case OPT_MIDDLEBUTTON_DISABLE: + options->middlebutton = 0; + break; case OPT_CLICK_METHOD: if (!optarg) { tools_usage(); @@ -297,6 +310,9 @@ tools_device_apply_config(struct libinput_device *device, options->natural_scroll); if (options->left_handed != -1) libinput_device_config_left_handed_set(device, options->left_handed); + if (options->middlebutton != -1) + libinput_device_config_middle_emulation_set_enabled(device, + options->middlebutton); if (options->click_method != -1) libinput_device_config_click_set_method(device, options->click_method); diff --git a/tools/shared.h b/tools/shared.h index caf48558..01edffc9 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -39,6 +39,7 @@ struct tools_options { int tapping; int natural_scroll; int left_handed; + int middlebutton; enum libinput_config_click_method click_method; double speed; }; From 75f15917a396136fbc0f1c56f5e654a89a320a44 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Apr 2015 16:38:44 +1000 Subject: [PATCH 041/165] evdev: add support for middle button emulation This is just the required framework, it's not hooked up to anything just yet. Hooking it up comes as separate commit to better detail why/when a device supports emulation. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/Makefile.am | 5 +- doc/middle-button-emulation.svg | 1315 +++++++++++++++++++++++++++++++ src/Makefile.am | 1 + src/evdev-middle-button.c | 716 +++++++++++++++++ src/evdev.c | 6 + src/evdev.h | 46 ++ test/litest.c | 6 + test/litest.h | 1 + test/touchpad.c | 3 + 9 files changed, 2098 insertions(+), 1 deletion(-) create mode 100644 doc/middle-button-emulation.svg create mode 100644 src/evdev-middle-button.c diff --git a/doc/Makefile.am b/doc/Makefile.am index f66b47f1..6e6f72d3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,4 +1,7 @@ -EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg +EXTRA_DIST = \ + middle-button-emulation.svg \ + touchpad-tap-state-machine.svg \ + touchpad-softbutton-state-machine.svg if BUILD_DOCS diff --git a/doc/middle-button-emulation.svg b/doc/middle-button-emulation.svg new file mode 100644 index 00000000..338af386 --- /dev/null +++ b/doc/middle-button-emulation.svg @@ -0,0 +1,1315 @@ + + + + + + + + + +
+
+ IDLE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ left button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ LEFT_DOWN
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ RIGHT_DOWN
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ PASSTHROUGH
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ right button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ timeout
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN_LEFT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_LEFT
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN_LEFT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ MIDDLE
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_MIDDLE
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ LUP_PENDING
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ RUP_PENDING
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ IDLE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ right button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ left button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ left button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ timeout
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ BTN_RIGHT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + + + +
+
+ BTN_RIGHT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_RIGHT
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + + + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ BTN_MIDDLE
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ IGNORE_LR
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ other button
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ PASSTHROUGH
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ IGNORE_R
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ IGNORE_L
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ left button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_LEFT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ BTN_LEFT
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ right button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ right button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_RIGHT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN_RIGHT
+ RELEASE
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + + + + + + + +
+
+ left button
+ release
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ other button
+ press
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN_LEFT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ BTN_RIGHT
+ PRESS
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ any button
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ all buttons
+ up?
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ no
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ yes
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ other button
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ other button
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + + + +
+
+ any button
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ BTN*
+ event
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ all buttons
+ up?
+
+
+
+ + [Not supported by viewer] +
+
+ + + + + + + + +
+
+ no
+
+
+ + [Not supported by viewer] +
+
+ + + + +
+
+ IDLE
+
+
+ + [Not supported by viewer] +
+
+ + + + + +
+
+ yes
+
+
+ + [Not supported by viewer] +
+
+
+
diff --git a/src/Makefile.am b/src/Makefile.am index 0f180cc4..90bdc9a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ libinput_la_SOURCES = \ libinput-private.h \ evdev.c \ evdev.h \ + evdev-middle-button.c \ evdev-mt-touchpad.c \ evdev-mt-touchpad.h \ evdev-mt-touchpad-tap.c \ diff --git a/src/evdev-middle-button.c b/src/evdev-middle-button.c new file mode 100644 index 00000000..328cf6c3 --- /dev/null +++ b/src/evdev-middle-button.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2014 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "evdev.h" + +#define MIDDLEBUTTON_TIMEOUT 50 + +/***************************************** + * BEFORE YOU EDIT THIS FILE, look at the state diagram in + * doc/middle-button-emulation-state-machine.svg, or online at + * https://drive.google.com/file/d/0B1NwWmji69nodUJncXRMc1FvY1k/view?usp=sharing + * (it's a http://draw.io diagram) + * + * Any changes in this file must be represented in the diagram. + * + * Note in regards to the state machine: it only handles left, right and + * emulated middle button clicks, all other button events are passed + * through. When in the PASSTHROUGH state, all events are passed through + * as-is. + */ + +#define CASE_RETURN_STRING(a) case a: return #a; + +static inline const char* +middlebutton_state_to_str(enum evdev_middlebutton_state state) +{ + switch (state) { + CASE_RETURN_STRING(MIDDLEBUTTON_IDLE); + CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_DOWN); + CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_DOWN); + CASE_RETURN_STRING(MIDDLEBUTTON_MIDDLE); + CASE_RETURN_STRING(MIDDLEBUTTON_LEFT_UP_PENDING); + CASE_RETURN_STRING(MIDDLEBUTTON_RIGHT_UP_PENDING); + CASE_RETURN_STRING(MIDDLEBUTTON_PASSTHROUGH); + CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_LR); + CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_L); + CASE_RETURN_STRING(MIDDLEBUTTON_IGNORE_R); + } + + return NULL; +} + +static inline const char* +middlebutton_event_to_str(enum evdev_middlebutton_event event) +{ + switch (event) { + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_DOWN); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_DOWN); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_OTHER); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_L_UP); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_R_UP); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_TIMEOUT); + CASE_RETURN_STRING(MIDDLEBUTTON_EVENT_ALL_UP); + } + + return NULL; +} + +static void +middlebutton_state_error(struct evdev_device *device, + enum evdev_middlebutton_event event) +{ + log_bug_libinput(device->base.seat->libinput, + "Invalid event %s in middle btn state %s\n", + middlebutton_event_to_str(event), + middlebutton_state_to_str(device->middlebutton.state)); +} + +static void +middlebutton_timer_set(struct evdev_device *device, uint64_t now) +{ + libinput_timer_set(&device->middlebutton.timer, + now + MIDDLEBUTTON_TIMEOUT); +} + +static void +middlebutton_timer_cancel(struct evdev_device *device) +{ + libinput_timer_cancel(&device->middlebutton.timer); +} + +static inline void +middlebutton_set_state(struct evdev_device *device, + enum evdev_middlebutton_state state, + uint64_t now) +{ + switch (state) { + case MIDDLEBUTTON_LEFT_DOWN: + case MIDDLEBUTTON_RIGHT_DOWN: + middlebutton_timer_set(device, now); + device->middlebutton.first_event_time = now; + break; + case MIDDLEBUTTON_IDLE: + case MIDDLEBUTTON_MIDDLE: + case MIDDLEBUTTON_LEFT_UP_PENDING: + case MIDDLEBUTTON_RIGHT_UP_PENDING: + case MIDDLEBUTTON_PASSTHROUGH: + case MIDDLEBUTTON_IGNORE_LR: + case MIDDLEBUTTON_IGNORE_L: + case MIDDLEBUTTON_IGNORE_R: + middlebutton_timer_cancel(device); + break; + } + + device->middlebutton.state = state; +} + +static void +middlebutton_post_event(struct evdev_device *device, + uint64_t now, + int button, + enum libinput_button_state state) +{ + evdev_pointer_notify_button(device, + now, + button, + state); +} + +static int +evdev_middlebutton_idle_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_set_state(device, MIDDLEBUTTON_LEFT_DOWN, time); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_DOWN, time); + break; + case MIDDLEBUTTON_EVENT_OTHER: + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + case MIDDLEBUTTON_EVENT_L_UP: + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + break; + } + + return 1; +} + +static int +evdev_middlebutton_ldown_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time); + break; + case MIDDLEBUTTON_EVENT_OTHER: + middlebutton_post_event(device, time, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_post_event(device, + device->middlebutton.first_event_time, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_post_event(device, time, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_post_event(device, + device->middlebutton.first_event_time, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_rdown_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_OTHER: + middlebutton_post_event(device, + device->middlebutton.first_event_time, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_post_event(device, + device->middlebutton.first_event_time, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_post_event(device, time, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_post_event(device, + device->middlebutton.first_event_time, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_middle_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_OTHER: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time); + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + middlebutton_set_state(device, + MIDDLEBUTTON_LEFT_UP_PENDING, + time); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + middlebutton_set_state(device, + MIDDLEBUTTON_RIGHT_UP_PENDING, + time); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_lup_pending_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time); + break; + case MIDDLEBUTTON_EVENT_OTHER: + middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time); + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_rup_pending_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_post_event(device, time, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_OTHER: + middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time); + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_passthrough_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + case MIDDLEBUTTON_EVENT_R_DOWN: + case MIDDLEBUTTON_EVENT_OTHER: + case MIDDLEBUTTON_EVENT_R_UP: + case MIDDLEBUTTON_EVENT_L_UP: + return 0; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time); + break; + } + + return 1; +} + +static int +evdev_middlebutton_ignore_lr_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_OTHER: + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_L, time); + break; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_R, time); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} + +static int +evdev_middlebutton_ignore_l_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_R_DOWN: + return 0; + case MIDDLEBUTTON_EVENT_OTHER: + case MIDDLEBUTTON_EVENT_R_UP: + return 0; + case MIDDLEBUTTON_EVENT_L_UP: + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + break; + case MIDDLEBUTTON_EVENT_TIMEOUT: + case MIDDLEBUTTON_EVENT_ALL_UP: + middlebutton_state_error(device, event); + break; + } + + return 1; +} +static int +evdev_middlebutton_ignore_r_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + switch (event) { + case MIDDLEBUTTON_EVENT_L_DOWN: + return 0; + case MIDDLEBUTTON_EVENT_R_DOWN: + middlebutton_state_error(device, event); + break; + case MIDDLEBUTTON_EVENT_OTHER: + return 0; + case MIDDLEBUTTON_EVENT_R_UP: + middlebutton_set_state(device, + MIDDLEBUTTON_PASSTHROUGH, + time); + break; + case MIDDLEBUTTON_EVENT_L_UP: + return 0; + case MIDDLEBUTTON_EVENT_TIMEOUT: + case MIDDLEBUTTON_EVENT_ALL_UP: + break; + } + + return 1; +} + +static int +evdev_middlebutton_handle_event(struct evdev_device *device, + uint64_t time, + enum evdev_middlebutton_event event) +{ + int rc; + enum evdev_middlebutton_state current; + + current = device->middlebutton.state; + + switch (current) { + case MIDDLEBUTTON_IDLE: + rc = evdev_middlebutton_idle_handle_event(device, time, event); + break; + case MIDDLEBUTTON_LEFT_DOWN: + rc = evdev_middlebutton_ldown_handle_event(device, time, event); + break; + case MIDDLEBUTTON_RIGHT_DOWN: + rc = evdev_middlebutton_rdown_handle_event(device, time, event); + break; + case MIDDLEBUTTON_MIDDLE: + rc = evdev_middlebutton_middle_handle_event(device, time, event); + break; + case MIDDLEBUTTON_LEFT_UP_PENDING: + rc = evdev_middlebutton_lup_pending_handle_event(device, + time, + event); + break; + case MIDDLEBUTTON_RIGHT_UP_PENDING: + rc = evdev_middlebutton_rup_pending_handle_event(device, + time, + event); + break; + case MIDDLEBUTTON_PASSTHROUGH: + rc = evdev_middlebutton_passthrough_handle_event(device, + time, + event); + break; + case MIDDLEBUTTON_IGNORE_LR: + rc = evdev_middlebutton_ignore_lr_handle_event(device, + time, + event); + break; + case MIDDLEBUTTON_IGNORE_L: + rc = evdev_middlebutton_ignore_l_handle_event(device, + time, + event); + break; + case MIDDLEBUTTON_IGNORE_R: + rc = evdev_middlebutton_ignore_r_handle_event(device, + time, + event); + break; + } + + log_debug(device->base.seat->libinput, + "middlebuttonstate: %s → %s → %s, rc %d\n", + middlebutton_state_to_str(current), + middlebutton_event_to_str(event), + middlebutton_state_to_str(device->middlebutton.state), + rc); + + return rc; +} + +static inline void +evdev_middlebutton_apply_config(struct evdev_device *device) +{ + if (device->middlebutton.want_enabled == + device->middlebutton.enabled) + return; + + if (device->middlebutton.button_mask != 0) + return; + + device->middlebutton.enabled = device->middlebutton.want_enabled; +} + +bool +evdev_middlebutton_filter_button(struct evdev_device *device, + uint64_t time, + int button, + enum libinput_button_state state) +{ + enum evdev_middlebutton_event event; + bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED; + int rc; + unsigned int bit = (button - BTN_LEFT); + uint32_t old_mask = 0; + + if (!device->middlebutton.enabled) + return false; + + switch (button) { + case BTN_LEFT: + if (is_press) + event = MIDDLEBUTTON_EVENT_L_DOWN; + else + event = MIDDLEBUTTON_EVENT_L_UP; + break; + case BTN_RIGHT: + if (is_press) + event = MIDDLEBUTTON_EVENT_R_DOWN; + else + event = MIDDLEBUTTON_EVENT_R_UP; + break; + + /* BTN_MIDDLE counts as "other" and resets middle button + * emulation */ + case BTN_MIDDLE: + default: + event = MIDDLEBUTTON_EVENT_OTHER; + break; + } + + if (button < BTN_LEFT || + bit >= sizeof(device->middlebutton.button_mask) * 8) { + log_bug_libinput(device->base.seat->libinput, + "Button mask too small for %d\n", + libevdev_event_code_get_name(EV_KEY, + button)); + return true; + } + + rc = evdev_middlebutton_handle_event(device, time, event); + + old_mask = device->middlebutton.button_mask; + if (is_press) + device->middlebutton.button_mask |= 1 << bit; + else + device->middlebutton.button_mask &= ~(1 << bit); + + if (old_mask != device->middlebutton.button_mask && + device->middlebutton.button_mask == 0) { + evdev_middlebutton_handle_event(device, + time, + MIDDLEBUTTON_EVENT_ALL_UP); + evdev_middlebutton_apply_config(device); + } + + return rc; +} + +static void +evdev_middlebutton_handle_timeout(uint64_t now, void *data) +{ + struct evdev_device *device = (struct evdev_device*)data; + + evdev_middlebutton_handle_event(device, + libinput_now(device->base.seat->libinput), + MIDDLEBUTTON_EVENT_TIMEOUT); +} + +static int +evdev_middlebutton_is_available(struct libinput_device *device) +{ + return 1; +} + +static enum libinput_config_status +evdev_middlebutton_set(struct libinput_device *device, + enum libinput_config_middle_emulation_state enable) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + + switch (enable) { + case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + evdev->middlebutton.want_enabled = true; + break; + case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + evdev->middlebutton.want_enabled = false; + break; + default: + return LIBINPUT_CONFIG_STATUS_INVALID; + } + + evdev_middlebutton_apply_config(evdev); + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static enum libinput_config_middle_emulation_state +evdev_middlebutton_get(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + + return evdev->middlebutton.enabled ? + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED : + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; +} + +static enum libinput_config_middle_emulation_state +evdev_middlebutton_get_default(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + + return evdev->middlebutton.enabled_default ? + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED : + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; +} + +void +evdev_init_middlebutton(struct evdev_device *device, + bool enable, + bool want_config) +{ + libinput_timer_init(&device->middlebutton.timer, + device->base.seat->libinput, + evdev_middlebutton_handle_timeout, + device); + device->middlebutton.enabled_default = enable; + device->middlebutton.want_enabled = enable; + device->middlebutton.enabled = enable; + + if (!want_config) + return; + + device->middlebutton.config.available = evdev_middlebutton_is_available; + device->middlebutton.config.set = evdev_middlebutton_set; + device->middlebutton.config.get = evdev_middlebutton_get; + device->middlebutton.config.get_default = evdev_middlebutton_get_default; + device->base.config.middle_emulation = &device->middlebutton.config; +} diff --git a/src/evdev.c b/src/evdev.c index 6f87484d..6ca874a1 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -143,6 +143,12 @@ evdev_pointer_notify_physical_button(struct evdev_device *device, int button, enum libinput_button_state state) { + if (evdev_middlebutton_filter_button(device, + time, + button, + state)) + return; + evdev_pointer_notify_button(device, time, button, state); } diff --git a/src/evdev.h b/src/evdev.h index f2ce9bcd..9b1dcc36 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -63,6 +63,29 @@ enum evdev_device_tags { EVDEV_TAG_TOUCHPAD_TRACKPOINT = (1 << 3), }; +enum evdev_middlebutton_state { + MIDDLEBUTTON_IDLE, + MIDDLEBUTTON_LEFT_DOWN, + MIDDLEBUTTON_RIGHT_DOWN, + MIDDLEBUTTON_MIDDLE, + MIDDLEBUTTON_LEFT_UP_PENDING, + MIDDLEBUTTON_RIGHT_UP_PENDING, + MIDDLEBUTTON_IGNORE_LR, + MIDDLEBUTTON_IGNORE_L, + MIDDLEBUTTON_IGNORE_R, + MIDDLEBUTTON_PASSTHROUGH, +}; + +enum evdev_middlebutton_event { + MIDDLEBUTTON_EVENT_L_DOWN, + MIDDLEBUTTON_EVENT_R_DOWN, + MIDDLEBUTTON_EVENT_OTHER, + MIDDLEBUTTON_EVENT_L_UP, + MIDDLEBUTTON_EVENT_R_UP, + MIDDLEBUTTON_EVENT_TIMEOUT, + MIDDLEBUTTON_EVENT_ALL_UP, +}; + struct mt_slot { int32_t seat_slot; struct device_coords point; @@ -158,6 +181,18 @@ struct evdev_device { void (*change_to_enabled)(struct evdev_device *device); } left_handed; + struct { + struct libinput_device_config_middle_emulation config; + /* middle-button emulation enabled */ + bool enabled; + bool enabled_default; + bool want_enabled; + enum evdev_middlebutton_state state; + struct libinput_timer timer; + uint32_t button_mask; + uint64_t first_event_time; + } middlebutton; + int dpi; /* HW resolution */ struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */ }; @@ -332,6 +367,17 @@ evdev_device_remove(struct evdev_device *device); void evdev_device_destroy(struct evdev_device *device); +bool +evdev_middlebutton_filter_button(struct evdev_device *device, + uint64_t time, + int button, + enum libinput_button_state state); + +void +evdev_init_middlebutton(struct evdev_device *device, + bool enabled, + bool want_config); + static inline double evdev_convert_to_mm(const struct input_absinfo *absinfo, double v) { diff --git a/test/litest.c b/test/litest.c index 5c4f84dd..12851071 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1436,6 +1436,12 @@ litest_timeout_edgescroll(void) msleep(300); } +void +litest_timeout_middlebutton(void) +{ + msleep(70); +} + void litest_push_event_frame(struct litest_device *dev) { diff --git a/test/litest.h b/test/litest.h index 84567be3..8cb11214 100644 --- a/test/litest.h +++ b/test/litest.h @@ -194,6 +194,7 @@ void litest_timeout_softbuttons(void); void litest_timeout_buttonscroll(void); void litest_timeout_edgescroll(void); void litest_timeout_finger_switch(void); +void litest_timeout_middlebutton(void); void litest_push_event_frame(struct litest_device *dev); void litest_pop_event_frame(struct litest_device *dev); diff --git a/test/touchpad.c b/test/touchpad.c index 4e425f58..ac8ffb92 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2895,6 +2895,9 @@ START_TEST(touchpad_left_handed_delayed) /* left-handed takes effect now */ litest_button_click(dev, BTN_RIGHT, 1); + libinput_dispatch(li); + litest_timeout_middlebutton(); + libinput_dispatch(li); litest_button_click(dev, BTN_LEFT, 1); libinput_dispatch(li); From 1ba7004dbc0af62e65221ac6fa0fb59204a5e81e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 14:41:39 +1000 Subject: [PATCH 042/165] evdev: enable middle button configuration on some devices Devices that have left and right buttons but no middle button get middle button emulation (without config). Devices that have a middle button too get a config option but default to off. Most mice have LMR set as buttons, regardless whether they have a middle button. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 6ca874a1..da33eaeb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1143,6 +1143,24 @@ fallback_dispatch_create(struct libinput_device *device) evdev_init_calibration(evdev_device, dispatch); evdev_init_sendevents(evdev_device, dispatch); + /* BTN_MIDDLE is set on mice even when it's not present. So + * we can only use the absense of BTN_MIDDLE to mean something, i.e. + * we enable it by default on anything that only has L&R. + * If we have L&R and no middle, we don't expose it as config + * option */ + if (libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_LEFT) && + libevdev_has_event_code(evdev_device->evdev, EV_KEY, BTN_RIGHT)) { + bool has_middle = libevdev_has_event_code(evdev_device->evdev, + EV_KEY, + BTN_MIDDLE); + bool want_config = has_middle; + bool enable_by_default = !has_middle; + + evdev_init_middlebutton(evdev_device, + enable_by_default, + want_config); + } + return dispatch; } From e6d503a72c77c584f0482a1f3a8a8e60c669bbfe Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 14:40:49 +1000 Subject: [PATCH 043/165] touchpad: enable middle button emulation on some touchpads If the touchpad has left/right physical buttons but no middle button, force middle button emulation - without a config option, it's always on. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 56a054ce..f64343cd 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -694,6 +694,10 @@ tp_init_buttons(struct tp_dispatch *tp, tp_init_top_softbuttons(tp, device, 1.0); + if (!tp->buttons.is_clickpad && + !libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) + evdev_init_middlebutton(tp->device, true, false); + tp_for_each_touch(tp, t) { t->button.state = BUTTON_STATE_NONE; libinput_timer_init(&t->button.timer, From a09476cdacaa372eead3b44cc693614d88593707 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 14 Apr 2015 14:43:53 +1000 Subject: [PATCH 044/165] test: middle button emulation tests Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/pointer.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) diff --git a/test/pointer.c b/test/pointer.c index a39cb2c7..557fed7b 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -866,6 +866,337 @@ START_TEST(pointer_accel_direction_change) } END_TEST +START_TEST(middlebutton) +{ + struct litest_device *device = litest_current_device(); + struct libinput *li = device->libinput; + enum libinput_config_status status; + unsigned int i; + const int btn[][4] = { + { BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT }, + { BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT }, + { BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT }, + { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT }, + }; + + status = libinput_device_config_middle_emulation_set_enabled( + device->libinput_device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + litest_drain_events(li); + + for (i = 0; i < ARRAY_LENGTH(btn); i++) { + litest_button_click(device, btn[i][0], true); + litest_button_click(device, btn[i][1], true); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + + litest_button_click(device, btn[i][2], false); + litest_button_click(device, btn[i][3], false); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(middlebutton_timeout) +{ + struct litest_device *device = litest_current_device(); + struct libinput *li = device->libinput; + enum libinput_config_status status; + unsigned int button; + + status = libinput_device_config_middle_emulation_set_enabled( + device->libinput_device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { + litest_drain_events(li); + litest_button_click(device, button, true); + litest_assert_empty_queue(li); + litest_timeout_middlebutton(); + + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_button_click(device, button, false); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(middlebutton_doubleclick) +{ + struct litest_device *device = litest_current_device(); + struct libinput *li = device->libinput; + enum libinput_config_status status; + unsigned int i; + const int btn[][4] = { + { BTN_LEFT, BTN_RIGHT, BTN_LEFT, BTN_RIGHT }, + { BTN_LEFT, BTN_RIGHT, BTN_RIGHT, BTN_LEFT }, + { BTN_RIGHT, BTN_LEFT, BTN_LEFT, BTN_RIGHT }, + { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT }, + }; + + status = libinput_device_config_middle_emulation_set_enabled( + device->libinput_device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + litest_drain_events(li); + + for (i = 0; i < ARRAY_LENGTH(btn); i++) { + litest_button_click(device, btn[i][0], true); + litest_button_click(device, btn[i][1], true); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + + litest_button_click(device, btn[i][2], false); + litest_button_click(device, btn[i][2], true); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_button_click(device, btn[i][3], false); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(middlebutton_middleclick) +{ + struct litest_device *device = litest_current_device(); + struct libinput *li = device->libinput; + enum libinput_config_status status; + unsigned int button; + + if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) + return; + + status = libinput_device_config_middle_emulation_set_enabled( + device->libinput_device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + /* one button down, then middle -> release buttons */ + for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { + /* release button before middle */ + litest_drain_events(li); + litest_button_click(device, button, true); + litest_button_click(device, BTN_MIDDLE, true); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + litest_button_click(device, button, false); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_button_click(device, BTN_MIDDLE, false); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + + /* release middle before button */ + litest_button_click(device, button, true); + litest_button_click(device, BTN_MIDDLE, true); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + litest_button_click(device, BTN_MIDDLE, false); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_button_click(device, button, false); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(middlebutton_middleclick_during) +{ + struct litest_device *device = litest_current_device(); + struct libinput *li = device->libinput; + enum libinput_config_status status; + unsigned int button; + + if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) + return; + + status = libinput_device_config_middle_emulation_set_enabled( + device->libinput_device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED) + return; + + litest_drain_events(li); + + /* trigger emulation, then real middle */ + for (button = BTN_LEFT; button <= BTN_RIGHT; button++) { + litest_button_click(device, BTN_LEFT, true); + litest_button_click(device, BTN_RIGHT, true); + + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_button_click(device, BTN_MIDDLE, true); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_assert_empty_queue(li); + + /* middle still down, release left/right */ + litest_button_click(device, button, false); + litest_assert_empty_queue(li); + litest_button_click(device, button, true); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_empty_queue(li); + + /* release both */ + litest_button_click(device, BTN_LEFT, false); + litest_button_click(device, BTN_RIGHT, false); + litest_assert_button_event(li, + button, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + + litest_button_click(device, BTN_MIDDLE, false); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(middlebutton_default_enabled) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int available; + enum libinput_config_middle_emulation_state deflt; + + available = libinput_device_config_middle_emulation_is_available(device); + ck_assert(available); + + if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) + deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + else + deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; + + status = libinput_device_config_middle_emulation_get_enabled(device); + ck_assert_int_eq(status, deflt); + + status = libinput_device_config_middle_emulation_get_default_enabled( + device); + ck_assert_int_eq(status, deflt); + + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + status = libinput_device_config_middle_emulation_set_enabled(device, 3); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); +} +END_TEST + +START_TEST(middlebutton_default_clickpad) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int available; + + available = libinput_device_config_middle_emulation_is_available(device); + ck_assert(!available); + + status = libinput_device_config_middle_emulation_get_enabled(device); + ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + status = libinput_device_config_middle_emulation_get_default_enabled( + device); + ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + + status = libinput_device_config_middle_emulation_set_enabled(device, 3); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); +} +END_TEST + +START_TEST(middlebutton_default_touchpad) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_status status; + int available; + + available = libinput_device_config_middle_emulation_is_available(device); + ck_assert(!available); + + if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) + return; + + status = libinput_device_config_middle_emulation_get_enabled( + device); + ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + status = libinput_device_config_middle_emulation_get_default_enabled( + device); + ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); +} +END_TEST + int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); @@ -893,5 +1224,13 @@ int main (int argc, char **argv) { litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY); litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_timeout, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_doubleclick, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_middleclick, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_middleclick_during, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_default_enabled, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); return litest_run(argc, argv); } From 88d1a526706c208c8ec435382e225bd84c5a218a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 16 Apr 2015 10:42:16 +1000 Subject: [PATCH 045/165] test: add litest_is_button_event Makes it easier from a caller to check for common things without all the other boilerplate code. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 26 +++++++++++++++++++------- test/litest.h | 4 ++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/test/litest.c b/test/litest.c index 12851071..2481f690 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1321,16 +1321,13 @@ litest_create_uinput_device(const char *name, struct input_id *id, ...) return uinput; } -void -litest_assert_button_event(struct libinput *li, unsigned int button, - enum libinput_button_state state) +struct libinput_event_pointer* +litest_is_button_event(struct libinput_event *event, + int button, + enum libinput_button_state state) { - struct libinput_event *event; struct libinput_event_pointer *ptrev; - litest_wait_for_event(li); - event = libinput_get_event(li); - ck_assert(event != NULL); ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_POINTER_BUTTON); @@ -1339,6 +1336,21 @@ litest_assert_button_event(struct libinput *li, unsigned int button, button); ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), state); + + return ptrev; +} + +void +litest_assert_button_event(struct libinput *li, unsigned int button, + enum libinput_button_state state) +{ + struct libinput_event *event; + + litest_wait_for_event(li); + event = libinput_get_event(li); + + litest_is_button_event(event, button, state); + libinput_event_destroy(event); } diff --git a/test/litest.h b/test/litest.h index 8cb11214..ca679836 100644 --- a/test/litest.h +++ b/test/litest.h @@ -172,6 +172,10 @@ void litest_wait_for_event(struct libinput *li); void litest_wait_for_event_of_type(struct libinput *li, ...); void litest_drain_events(struct libinput *li); void litest_assert_empty_queue(struct libinput *li); +struct libinput_event_pointer * litest_is_button_event( + struct libinput_event *event, + int button, + enum libinput_button_state state); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); From abdca333877e9cbd7df0d9c295fa158bec96a407 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 16 Apr 2015 10:19:11 +1000 Subject: [PATCH 046/165] touchpad: introduce MULTITAP for multi-tap-and-drag Once we have a doubletap, enter a loop in the state machine where we can tap multiple times and either get a multi-click or a multi-click drag-and-drop. The sequence down/up down/up down/up produces a triple-click. The sequence down/up down/up down/up down produces a triple-click with a button down for dragging. Yes, that glorious octuple-tap-and-drag, it is now possible. World domination has been achieved, thank you for playing. We don't know when we finish tapping now, so add a timeout to send the last click event once the finger has been released for the last time. This guarantees that the timestamp of the last button down is later than the last release. This avoids the bug fixed in synaptics commit xf86-input-synaptics-1.8.0-21-g37d34f0 (some application don't handle doubletap correctly without the timestamps). This works for double-tap immediately, for multi-tap we need to remember the timestamp of the first press event and use it for the release event so that there's a forced gap between the release and the second press. https://bugs.freedesktop.org/show_bug.cgi?id=89511 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/touchpad-tap-state-machine.svg | 1406 +++++++++++++++++----------- src/evdev-mt-touchpad-tap.c | 88 +- src/evdev-mt-touchpad.h | 3 + test/touchpad.c | 394 ++++++++ 4 files changed, 1353 insertions(+), 538 deletions(-) diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 7aecefc1..b168b4f6 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,756 +1,1094 @@ - - + + + + + - - - - - - - + + + + + + + - IDLE + + IDLE - + - TOUCH + + TOUCH - + - first - finger down + + first + + finger down - - - + + + - finger up + + finger up - - - + + + - button 1 - press + + button 1 + + press - + - timeout + + timeout - - - + + + - move > - threshold + + move > + + threshold - - - + + + - second - finger down + + second + + finger down - - - + + + - TOUCH_2 + + TOUCH_2 - + - second - finger up + + second + + finger up - - - + + + - button 2 - press + + button 2 + + press - + - move > - threshold + + move > + + threshold - + - timeout + + timeout - - - - - + + + + + - button 1 - release + + button 1 + + release - + - button 2 - release + + button 2 + + release - - - - - + + + + + - TAPPED + + TAPPED - + - timeout + + timeout - - - + + + - first - finger down + + first + + finger down - - - + + + - DRAGGING + + DRAGGING - + - first - finger up + + first + + finger up - + - btn1 - release + + btn1 + + release - - - - - - - + + + + + + + - IDLE + + IDLE - + - third - finger down + + third + + finger down - - - + + + - TOUCH_3 + + TOUCH_3 - - - + + + - button 3 - press + + button 3 + + press - + - button 3 - release + + button 3 + + release - - - + + + - move > - threshold + + move > + + threshold - - - + + + - IDLE + + IDLE - + - timeout + + timeout - - - + + + - first - finger up + + first + + finger up - - - + + + - IDLE + + IDLE - + - fourth - finger down + + fourth + + finger down - - - - - + + + + + - DRAGGING_OR_DOUBLETAP + + DRAGGING_OR_DOUBLETAP - - - + + + - timeout + + timeout - - - + + + - first - finger up + + first + + finger up - - - + + + - button 1 - release + + button 1 + + release - + - button 1 - press + + button 1 + + press - + - btn1 - release + + btn1 + + release - - - - - - - - - + + + - second - finger down + + second + + finger down - - - + + + - move > - threshold + + move > + + threshold - - - - - + + + + + - HOLD + + HOLD - + - first - finger up + + first + + finger up - - - - - + + + + + - second - finger down + + second + + finger down - - - - - - - + + + + + + + - TOUCH_2_HOLD + + TOUCH_2_HOLD - + - second - finger up + + second + + finger up - - - + + + - first - finger up + + first + + finger up - - - - - - - + + + + + + + - third - finger down + + third + + finger down - - - - - - - + + + + + + + - TOUCH_3_HOLD + + TOUCH_3_HOLD - - - + + + - fourth - finger down + + fourth + + finger down - + - DEAD + + DEAD - - - - - - - + + + + + + + - any finger up + + any finger up - + - fourth - finger up + + fourth + + finger up - + - any finger up + + any finger up - - - - + + + + - - yes + + + yes - + - any finger up + + any finger up - - - - - - - - - + + + + + + + + + - IDLE + + IDLE - + - if finger - count == 0 + + if finger + + count == 0 - - - - - - - + + + + + + + - second - finger up + + second + + finger up - + - DRAGGING_2 + + DRAGGING_2 - - - - - + + + + + - first - finger up + + first + + finger up - - - - - - - + + + + + + + - second - finger down + + second + + finger down - - - - - - - + + + + + + + - third - finger down + + third + + finger down - - - + + + - btn1 - release + + btn1 + + release - - - + + + - clickpad - button - press + + phys + + button + + press - - - - - - - - - - - - - + + + + + + + + + + + + + - clickpad - button - press + + phys + + button + + press - - - + + + - button 1 - release + + button 1 + + release - - - - - - - - - - - + + + + + + + + + + + - DRAGGING_WAIT + + DRAGGING_WAIT - + - timeout + + timeout - - - - - - - + + + + + + + - first - finger down + + first + + finger down - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - + - TOUCH_IDLE + + TOUCH_IDLE - - - - - - - - - + + + + + + + + + - TOUCH_DEAD + + TOUCH_DEAD - - - - - - - - - - + + + + + + + + + + - - yes + + + yes - + - TOUCH_DEAD + + TOUCH_DEAD - - - - - - - + + + + + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - - - + + + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - that finger - TOUCH_IDLE + + that finger + + TOUCH_IDLE - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - - - - - - - + + + + + + + - that finger - TOUCH_IDLE + + that finger + + TOUCH_IDLE - - - - + + + + - - no + + + no - + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - - - - - + + + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - - - + + + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - TOUCH_IDLE + + TOUCH_IDLE - - - + + + - TOUCH_TOUCH + + TOUCH_TOUCH - - - + + + - that finger - TOUCH_IDLE + + that finger + + TOUCH_IDLE - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - + - TOUCH_DEAD + + TOUCH_DEAD - + - TOUCH_DEAD + + TOUCH_DEAD - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - - - + + + - state == - TOUCH_TOUCH + + state == + + TOUCH_TOUCH - + - that finger state == - TOUCH_TOUCH + + that finger state == + + TOUCH_TOUCH - - + + - - no + + + no - + - TOUCH_DEAD + + TOUCH_DEAD - - - + + + - TOUCH_DEAD + + TOUCH_DEAD - + - TOUCH_DEAD + + TOUCH_DEAD + + + + first + + finger down + + + + + MULTITAP + + + + + + + + + timeout + + + + + + + + + IDLE + + + + + + + + + + + MULTITAP_DOWN + + + + + button 1 + + press + + + + + + + + + first + + finger up + + + + + + + button 1 + + release + + + + + + + timeout + + + + + second + + finger down + + + + + move > + + threshold + + + + + + + + + + + button 1 + + release + + + + + button 1 + + press + + + + + + + + + + + button 1 + + release + + + + + button 1 + + press + + + + + + + + + + + button 1 + + release + + + + + button 1 + + press + + + + + + + + + + + + + TOUCH_TOUCH + + + + + + + TOUCH_IDLE + + + + + + + phys + + button + + press + + + + + + + diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 49fabb50..0f25e267 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -74,6 +74,8 @@ tap_state_to_str(enum tp_tap_state state) { CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT); CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP); CASE_RETURN_STRING(TAP_STATE_DRAGGING_2); + CASE_RETURN_STRING(TAP_STATE_MULTITAP); + CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN); CASE_RETURN_STRING(TAP_STATE_DEAD); } return NULL; @@ -351,11 +353,9 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, tp->tap.state = TAP_STATE_DRAGGING_2; break; case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_IDLE; + tp->tap.state = TAP_STATE_MULTITAP; tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); - tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_clear_timer(tp); + tp_tap_set_timer(tp, time); break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: @@ -443,6 +443,79 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, } } +static void +tp_tap_multitap_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + struct libinput *libinput = tp->device->base.seat->libinput; + + switch (event) { + case TAP_EVENT_RELEASE: + log_bug_libinput(libinput, + "invalid tap event, no fingers are down\n"); + break; + case TAP_EVENT_TOUCH: + tp->tap.state = TAP_STATE_MULTITAP_DOWN; + tp->tap.multitap_last_time = time; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_MOTION: + log_bug_libinput(libinput, + "invalid tap event, no fingers are down\n"); + break; + case TAP_EVENT_TIMEOUT: + tp->tap.state = TAP_STATE_IDLE; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + break; + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_IDLE; + tp_tap_clear_timer(tp); + break; + } +} + +static void +tp_tap_multitap_down_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, + uint64_t time) +{ + switch (event) { + case TAP_EVENT_RELEASE: + tp->tap.state = TAP_STATE_MULTITAP; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_TOUCH: + tp->tap.state = TAP_STATE_DRAGGING_2; + tp_tap_notify(tp, + tp->tap.multitap_last_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_clear_timer(tp); + break; + case TAP_EVENT_MOTION: + case TAP_EVENT_TIMEOUT: + tp->tap.state = TAP_STATE_DRAGGING; + tp_tap_notify(tp, + tp->tap.multitap_last_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_clear_timer(tp); + break; + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_DEAD; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + tp_tap_clear_timer(tp); + break; + } +} + static void tp_tap_dead_handle_event(struct tp_dispatch *tp, struct tp_touch *t, @@ -511,6 +584,12 @@ tp_tap_handle_event(struct tp_dispatch *tp, case TAP_STATE_DRAGGING_2: tp_tap_dragging2_handle_event(tp, t, event, time); break; + case TAP_STATE_MULTITAP: + tp_tap_multitap_handle_event(tp, t, event, time); + break; + case TAP_STATE_MULTITAP_DOWN: + tp_tap_multitap_down_handle_event(tp, t, event, time); + break; case TAP_STATE_DEAD: tp_tap_dead_handle_event(tp, t, event, time); break; @@ -610,6 +689,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) case TAP_STATE_DRAGGING_OR_DOUBLETAP: case TAP_STATE_TOUCH_2: case TAP_STATE_TOUCH_3: + case TAP_STATE_MULTITAP_DOWN: filter_motion = 1; break; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 7b7600c3..97b17cde 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -97,6 +97,8 @@ enum tp_tap_state { TAP_STATE_DRAGGING, TAP_STATE_DRAGGING_WAIT, TAP_STATE_DRAGGING_2, + TAP_STATE_MULTITAP, + TAP_STATE_MULTITAP_DOWN, TAP_STATE_DEAD, /**< finger count exceeded */ }; @@ -256,6 +258,7 @@ struct tp_dispatch { enum tp_tap_state state; uint32_t buttons_pressed; unsigned int tap_finger_count; + uint64_t multitap_last_time; } tap; struct { diff --git a/test/touchpad.c b/test/touchpad.c index ac8ffb92..5ff4a366 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -123,6 +123,394 @@ START_TEST(touchpad_1fg_tap) } END_TEST +START_TEST(touchpad_1fg_doubletap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime, curtime; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_timeout_tap(); + + libinput_dispatch(li); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + oldtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_le(oldtime, curtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_lt(oldtime, curtime); + oldtime = curtime; + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_le(oldtime, curtime); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_1fg_multitap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 8; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + litest_timeout_tap(); + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(touchpad_1fg_multitap_n_drag_move) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 8; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(touchpad_1fg_multitap_n_drag_2fg) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 8; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + msleep(10); + litest_touch_down(dev, 1, 70, 50); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 1, 70, 50, 90, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 1); + litest_touch_up(dev, 0); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(touchpad_1fg_multitap_n_drag_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 8; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_touch_up(dev, 1); + litest_timeout_tap(); + + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(touchpad_1fg_multitap_n_drag_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 8; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + } +} +END_TEST + START_TEST(touchpad_1fg_tap_n_drag) { struct litest_device *dev = litest_current_device(); @@ -3636,6 +4024,12 @@ int main(int argc, char **argv) { litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); From 776bf4938dc4f3228fbfb45a5df4d18639b146de Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 17 Apr 2015 15:59:36 +1000 Subject: [PATCH 047/165] evdev: accept but disable ABS_MISC and above for min/max == 0 This is sort-of legitimate, so simply disable the axes and continue. Any real axis we require to have a real range. https://bugs.freedesktop.org/show_bug.cgi?id=90090 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 26 ++++- test/Makefile.am | 1 + test/device.c | 4 +- test/litest-mouse-roccat.c | 205 +++++++++++++++++++++++++++++++++++++ test/litest.c | 2 + test/litest.h | 1 + 6 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 test/litest-mouse-roccat.c diff --git a/src/evdev.c b/src/evdev.c index da33eaeb..1a7282d8 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1513,11 +1513,27 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code) absinfo = libevdev_get_abs_info(evdev, code); if (absinfo->minimum == absinfo->maximum) { - log_bug_kernel(device->base.seat->libinput, - "Device '%s' has min == max on %s\n", - device->devname, - libevdev_event_code_get_name(EV_ABS, code)); - return -1; + /* Some devices have a sort-of legitimate min/max of 0 for + * ABS_MISC and above (e.g. Roccat Kone XTD). Don't ignore + * them, simply disable the axes so we won't get events, + * we don't know what to do with them anyway. + */ + if (absinfo->minimum == 0 && + code >= ABS_MISC && code < ABS_MT_SLOT) { + log_info(device->base.seat->libinput, + "Disabling EV_ABS %#x on device '%s' (min == max == 0)\n", + code, + device->devname); + libevdev_disable_event_code(device->evdev, + EV_ABS, + code); + } else { + log_bug_kernel(device->base.seat->libinput, + "Device '%s' has min == max on %s\n", + device->devname, + libevdev_event_code_get_name(EV_ABS, code)); + return -1; + } } return 0; diff --git a/test/Makefile.am b/test/Makefile.am index fdfb84cf..a616c5c4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,6 +19,7 @@ liblitest_la_SOURCES = \ litest-keyboard.c \ litest-keyboard-razer-blackwidow.c \ litest-mouse.c \ + litest-mouse-roccat.c \ litest-ms-surface-cover.c \ litest-protocol-a-touch-screen.c \ litest-qemu-usb-tablet.c \ diff --git a/test/device.c b/test/device.c index ed3c2e02..b6031896 100644 --- a/test/device.c +++ b/test/device.c @@ -831,9 +831,7 @@ START_TEST(abs_device_no_range) li = litest_create_context(); litest_disable_log_handler(li); - for (code = 0; code < ABS_MT_SLOT; code++) { - if (code == ABS_MISC) - continue; + for (code = 0; code < ABS_MISC; code++) { absinfo[2].value = code; uinput = litest_create_uinput_abs_device("test device", NULL, absinfo, diff --git a/test/litest-mouse-roccat.c b/test/litest-mouse-roccat.c new file mode 100644 index 00000000..adccd3d1 --- /dev/null +++ b/test/litest-mouse-roccat.c @@ -0,0 +1,205 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "litest.h" +#include "litest-int.h" + +static void litest_mouse_roccat_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_MOUSE_ROCCAT); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x1e7d, + .product = 0x2e22, +}; + +static int events[] = { + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_REL, REL_HWHEEL, + EV_REL, REL_DIAL, + EV_KEY, KEY_ESC, + EV_KEY, KEY_ENTER, + EV_KEY, KEY_KPMINUS, + EV_KEY, KEY_KPPLUS, + EV_KEY, KEY_UP, + EV_KEY, KEY_LEFT, + EV_KEY, KEY_RIGHT, + EV_KEY, KEY_DOWN, + EV_KEY, KEY_MUTE, + EV_KEY, KEY_VOLUMEDOWN, + EV_KEY, KEY_VOLUMEUP, + EV_KEY, KEY_POWER, + EV_KEY, KEY_PAUSE, + EV_KEY, KEY_STOP, + EV_KEY, KEY_PROPS, + EV_KEY, KEY_UNDO, + EV_KEY, KEY_COPY, + EV_KEY, KEY_OPEN, + EV_KEY, KEY_PASTE, + EV_KEY, KEY_FIND, + EV_KEY, KEY_CUT, + EV_KEY, KEY_HELP, + EV_KEY, KEY_MENU, + EV_KEY, KEY_CALC, + EV_KEY, KEY_SLEEP, + EV_KEY, KEY_FILE, + EV_KEY, KEY_WWW, + EV_KEY, KEY_COFFEE, + EV_KEY, KEY_MAIL, + EV_KEY, KEY_BOOKMARKS, + EV_KEY, KEY_BACK, + EV_KEY, KEY_FORWARD, + EV_KEY, KEY_EJECTCD, + EV_KEY, KEY_NEXTSONG, + EV_KEY, KEY_PLAYPAUSE, + EV_KEY, KEY_PREVIOUSSONG, + EV_KEY, KEY_STOPCD, + EV_KEY, KEY_RECORD, + EV_KEY, KEY_REWIND, + EV_KEY, KEY_PHONE, + EV_KEY, KEY_CONFIG, + EV_KEY, KEY_HOMEPAGE, + EV_KEY, KEY_REFRESH, + EV_KEY, KEY_EXIT, + EV_KEY, KEY_SCROLLUP, + EV_KEY, KEY_SCROLLDOWN, + EV_KEY, KEY_NEW, + EV_KEY, KEY_CLOSE, + EV_KEY, KEY_PLAY, + EV_KEY, KEY_FASTFORWARD, + EV_KEY, KEY_BASSBOOST, + EV_KEY, KEY_PRINT, + EV_KEY, KEY_CAMERA, + EV_KEY, KEY_CHAT, + EV_KEY, KEY_SEARCH, + EV_KEY, KEY_FINANCE, + EV_KEY, KEY_BRIGHTNESSDOWN, + EV_KEY, KEY_BRIGHTNESSUP, + EV_KEY, KEY_KBDILLUMTOGGLE, + EV_KEY, KEY_SAVE, + EV_KEY, KEY_DOCUMENTS, + EV_KEY, KEY_UNKNOWN, + EV_KEY, KEY_VIDEO_NEXT, + EV_KEY, KEY_BRIGHTNESS_AUTO, + EV_KEY, BTN_0, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_SIDE, + EV_KEY, BTN_EXTRA, + EV_KEY, KEY_SELECT, + EV_KEY, KEY_GOTO, + EV_KEY, KEY_INFO, + EV_KEY, KEY_PROGRAM, + EV_KEY, KEY_PVR, + EV_KEY, KEY_SUBTITLE, + EV_KEY, KEY_ZOOM, + EV_KEY, KEY_KEYBOARD, + EV_KEY, KEY_PC, + EV_KEY, KEY_TV, + EV_KEY, KEY_TV2, + EV_KEY, KEY_VCR, + EV_KEY, KEY_VCR2, + EV_KEY, KEY_SAT, + EV_KEY, KEY_CD, + EV_KEY, KEY_TAPE, + EV_KEY, KEY_TUNER, + EV_KEY, KEY_PLAYER, + EV_KEY, KEY_DVD, + EV_KEY, KEY_AUDIO, + EV_KEY, KEY_VIDEO, + EV_KEY, KEY_MEMO, + EV_KEY, KEY_CALENDAR, + EV_KEY, KEY_RED, + EV_KEY, KEY_GREEN, + EV_KEY, KEY_YELLOW, + EV_KEY, KEY_BLUE, + EV_KEY, KEY_CHANNELUP, + EV_KEY, KEY_CHANNELDOWN, + EV_KEY, KEY_LAST, + EV_KEY, KEY_NEXT, + EV_KEY, KEY_RESTART, + EV_KEY, KEY_SLOW, + EV_KEY, KEY_SHUFFLE, + EV_KEY, KEY_PREVIOUS, + EV_KEY, KEY_VIDEOPHONE, + EV_KEY, KEY_GAMES, + EV_KEY, KEY_ZOOMIN, + EV_KEY, KEY_ZOOMOUT, + EV_KEY, KEY_ZOOMRESET, + EV_KEY, KEY_WORDPROCESSOR, + EV_KEY, KEY_EDITOR, + EV_KEY, KEY_SPREADSHEET, + EV_KEY, KEY_GRAPHICSEDITOR, + EV_KEY, KEY_PRESENTATION, + EV_KEY, KEY_DATABASE, + EV_KEY, KEY_NEWS, + EV_KEY, KEY_VOICEMAIL, + EV_KEY, KEY_ADDRESSBOOK, + EV_KEY, KEY_MESSENGER, + EV_KEY, KEY_DISPLAYTOGGLE, + EV_KEY, KEY_SPELLCHECK, + EV_KEY, KEY_LOGOFF, + EV_KEY, KEY_MEDIA_REPEAT, + EV_KEY, KEY_IMAGES, + EV_KEY, KEY_BUTTONCONFIG, + EV_KEY, KEY_TASKMANAGER, + EV_KEY, KEY_JOURNAL, + EV_KEY, KEY_CONTROLPANEL, + EV_KEY, KEY_APPSELECT, + EV_KEY, KEY_SCREENSAVER, + EV_KEY, KEY_VOICECOMMAND, + EV_KEY, KEY_BRIGHTNESS_MIN, + EV_KEY, KEY_BRIGHTNESS_MAX, + -1 , -1, +}; + +static struct input_absinfo absinfo[] = { + { ABS_VOLUME, 0, 572, 0, 0, 0 }, + { ABS_MISC, 0, 0, 0, 0, 0 }, + { ABS_MISC + 1, 0, 0, 0, 0, 0 }, + { ABS_MISC + 2, 0, 0, 0, 0, 0 }, + { ABS_MISC + 3, 0, 0, 0, 0, 0 }, + { .value = -1 } +}; + +struct litest_test_device litest_mouse_roccat_device = { + .type = LITEST_MOUSE_ROCCAT, + .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL, + .shortname = "mouse_roccat", + .setup = litest_mouse_roccat_setup, + .interface = NULL, + + .name = "ROCCAT ROCCAT Kone XTD", + .id = &input_id, + .absinfo = absinfo, + .events = events, +}; diff --git a/test/litest.c b/test/litest.c index 2481f690..235ccb9a 100644 --- a/test/litest.c +++ b/test/litest.c @@ -100,6 +100,7 @@ extern struct litest_test_device litest_protocol_a_screen; extern struct litest_test_device litest_wacom_finger_device; extern struct litest_test_device litest_keyboard_blackwidow_device; extern struct litest_test_device litest_wheel_only_device; +extern struct litest_test_device litest_mouse_roccat_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -121,6 +122,7 @@ struct litest_test_device* devices[] = { &litest_wacom_finger_device, &litest_keyboard_blackwidow_device, &litest_wheel_only_device, + &litest_mouse_roccat_device, NULL, }; diff --git a/test/litest.h b/test/litest.h index ca679836..e5a80305 100644 --- a/test/litest.h +++ b/test/litest.h @@ -55,6 +55,7 @@ enum litest_device_type { LITEST_WACOM_FINGER = -19, LITEST_KEYBOARD_BLACKWIDOW = -20, LITEST_WHEEL_ONLY = -21, + LITEST_MOUSE_ROCCAT = -22, }; enum litest_device_feature { From 476d8b9ecabab5473eee048dd0e85cce1e4cbe4a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 11:57:23 +1000 Subject: [PATCH 048/165] tools: add --set-scroll-method commandline flag Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/shared.c | 30 ++++++++++++++++++++++++++++++ tools/shared.h | 1 + 2 files changed, 31 insertions(+) diff --git a/tools/shared.c b/tools/shared.c index d8d23a7c..15ba8bc5 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -46,6 +46,7 @@ enum options { OPT_MIDDLEBUTTON_ENABLE, OPT_MIDDLEBUTTON_DISABLE, OPT_CLICK_METHOD, + OPT_SCROLL_METHOD, OPT_SPEED, }; @@ -76,6 +77,7 @@ tools_usage() "--enable-middlebutton\n" "--disable-middlebutton.... enable/disable middle button emulation\n" "--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n" + "--set-scroll-method=[none|twofinger|edge|button] ... set the desired scroll method\n" "--set-speed=.... set pointer acceleration speed\n" "\n" "These options apply to all applicable devices, if a feature\n" @@ -96,6 +98,7 @@ tools_init_options(struct tools_options *options) options->left_handed = -1; options->middlebutton = -1; options->click_method = -1; + options->scroll_method = -1; options->backend = BACKEND_UDEV; options->seat = "seat0"; options->speed = 0.0; @@ -121,6 +124,7 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) { "enable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_ENABLE }, { "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_DISABLE }, { "set-click-method", 1, 0, OPT_CLICK_METHOD }, + { "set-scroll-method", 1, 0, OPT_SCROLL_METHOD }, { "speed", 1, 0, OPT_SPEED }, { 0, 0, 0, 0} }; @@ -193,6 +197,28 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) return 1; } break; + case OPT_SCROLL_METHOD: + if (!optarg) { + tools_usage(); + return 1; + } + if (strcmp(optarg, "none") == 0) { + options->scroll_method = + LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } else if (strcmp(optarg, "twofinger") == 0) { + options->scroll_method = + LIBINPUT_CONFIG_SCROLL_2FG; + } else if (strcmp(optarg, "edge") == 0) { + options->scroll_method = + LIBINPUT_CONFIG_SCROLL_EDGE; + } else if (strcmp(optarg, "button") == 0) { + options->scroll_method = + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + } else { + tools_usage(); + return 1; + } + break; case OPT_SPEED: if (!optarg) { tools_usage(); @@ -317,6 +343,10 @@ tools_device_apply_config(struct libinput_device *device, if (options->click_method != -1) libinput_device_config_click_set_method(device, options->click_method); + if (options->scroll_method != -1) + libinput_device_config_scroll_set_method(device, + options->scroll_method); + if (libinput_device_config_accel_is_available(device)) libinput_device_config_accel_set_speed(device, options->speed); diff --git a/tools/shared.h b/tools/shared.h index 01edffc9..7b037882 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -41,6 +41,7 @@ struct tools_options { int left_handed; int middlebutton; enum libinput_config_click_method click_method; + enum libinput_config_scroll_method scroll_method; double speed; }; From 3b7095f33f2dc83e45591071037323c42429e082 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 12:05:36 +1000 Subject: [PATCH 049/165] test: enable edge scrolling in all edge scroll tests Just to make sure it is enabled (it should be anyway). Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/touchpad.c b/test/touchpad.c index 5ff4a366..aef8e1ea 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2638,12 +2638,25 @@ START_TEST(touchpad_scroll_natural) } END_TEST +static void +enable_edge_scroll(struct litest_device *dev) +{ + enum libinput_config_status status; + struct libinput_device *device = dev->libinput_device; + + status = libinput_device_config_scroll_set_method(device, + LIBINPUT_CONFIG_SCROLL_EDGE); + ck_assert_int_eq(status, + LIBINPUT_CONFIG_STATUS_SUCCESS); +} + START_TEST(touchpad_edge_scroll) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; litest_drain_events(li); + enable_edge_scroll(dev); litest_touch_down(dev, 0, 99, 20); litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0); @@ -2687,6 +2700,7 @@ START_TEST(touchpad_edge_scroll_timeout) struct libinput_event_pointer *ptrev; litest_drain_events(li); + enable_edge_scroll(dev); litest_touch_down(dev, 0, 99, 20); libinput_dispatch(li); @@ -2732,6 +2746,7 @@ START_TEST(touchpad_edge_scroll_no_motion) struct libinput *li = dev->libinput; litest_drain_events(li); + enable_edge_scroll(dev); litest_touch_down(dev, 0, 99, 10); litest_touch_move_to(dev, 0, 99, 10, 99, 70, 10, 0); @@ -2753,6 +2768,7 @@ START_TEST(touchpad_edge_scroll_no_edge_after_motion) struct libinput *li = dev->libinput; litest_drain_events(li); + enable_edge_scroll(dev); /* moving into the edge zone must not trigger scroll events */ litest_touch_down(dev, 0, 20, 20); @@ -2774,6 +2790,7 @@ START_TEST(touchpad_edge_scroll_source) struct libinput_event_pointer *ptrev; litest_drain_events(li); + enable_edge_scroll(dev); litest_touch_down(dev, 0, 99, 20); litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0); From 8fce9e2809b44a03fbe4ed0442f2af079a0837f1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 13:53:55 +1000 Subject: [PATCH 050/165] test: extend edge scrolling tests to all non-clickpads The single finger requirement dates back to when we couldn't configure the scroll method. Now we can, so let's run the tests on as many suitable devices as possible. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index aef8e1ea..2d25a177 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4126,11 +4126,11 @@ int main(int argc, char **argv) { litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:scroll", touchpad_scroll_natural, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); - litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY); - litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY); - litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY); - litest_add("touchpad:scroll", touchpad_edge_scroll_timeout, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY); - litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY); + litest_add("touchpad:scroll", touchpad_edge_scroll, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:scroll", touchpad_edge_scroll_timeout, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD); From 4cdb810bc8cc5106feb04b65403fa186c1f58507 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 14:55:49 +1000 Subject: [PATCH 051/165] test: adjust the movement size for edge scroll timeout testing The goal of this test is to make sure that the deltas are less than 5, which is the scroll trigger for movement-based edge scrolling. The litest suite takes percentages of the device, so use a scale factor to change how far we move on the tablet. The wacom tablet is 141mm, the movement must be smaller to provide small-enough deltas. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/touchpad.c b/test/touchpad.c index 2d25a177..88ae2b16 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2698,6 +2698,18 @@ START_TEST(touchpad_edge_scroll_timeout) struct libinput *li = dev->libinput; struct libinput_event *event; struct libinput_event_pointer *ptrev; + double width = 0, height = 0; + int y_movement = 30; /* in percent of height */ + + /* account for different touchpad heights, let's move 100% on a 15mm + high touchpad, less on anything else. This number is picked at + random, we just want deltas less than 5. + */ + if (libinput_device_get_size(dev->libinput_device, + &width, + &height) != -1) { + y_movement = 100 * 15/height; + } litest_drain_events(li); enable_edge_scroll(dev); @@ -2707,7 +2719,7 @@ START_TEST(touchpad_edge_scroll_timeout) litest_timeout_edgescroll(); libinput_dispatch(li); - litest_touch_move_to(dev, 0, 99, 20, 99, 80, 60, 10); + litest_touch_move_to(dev, 0, 99, 20, 99, 20 + y_movement, 60, 10); litest_touch_up(dev, 0); libinput_dispatch(li); From c2f267b4432daacce079098480f35d7d242c3d3c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 12:07:55 +1000 Subject: [PATCH 052/165] touchpad: don't post 2fg scrolling when edge scrolling is enabled https://bugs.freedesktop.org/show_bug.cgi?id=90070 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-gestures.c | 6 ++++++ test/touchpad.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index e01b9218..d332186b 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -110,6 +110,9 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) { struct normalized_coords delta; + if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) + return; + delta = tp_get_average_touches_delta(tp); delta = tp_filter_motion(tp, &delta, time); @@ -153,6 +156,9 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) void tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) { + if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) + return; + evdev_stop_scroll(tp->device, time, LIBINPUT_POINTER_AXIS_SOURCE_FINGER); diff --git a/test/touchpad.c b/test/touchpad.c index 88ae2b16..f3d2e6a2 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2821,6 +2821,26 @@ START_TEST(touchpad_edge_scroll_source) } END_TEST +START_TEST(touchpad_edge_scroll_no_2fg) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + enable_edge_scroll(dev); + + litest_touch_down(dev, 0, 20, 20); + litest_touch_down(dev, 1, 40, 20); + litest_touch_move_two_touches(dev, 20, 20, 40, 20, 20, 30, 10, 3); + libinput_dispatch(li); + litest_touch_up(dev, 0); + litest_touch_up(dev, 1); + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_tap_is_available) { struct litest_device *dev = litest_current_device(); @@ -4143,6 +4163,7 @@ int main(int argc, char **argv) { litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:scroll", touchpad_edge_scroll_timeout, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("touchpad:scroll", touchpad_edge_scroll_no_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_CLICKPAD); litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD); From 9ccd6c0cd2f2f2507d6408be38ade362b4c8a325 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 18:01:26 +1000 Subject: [PATCH 053/165] test: mix mixing of middle buttons state vs config status enum types Coverity complaint: mixed_enums: Mixing enum types enum libinput_config_middle_emulation_state and enum libinput_config_status Signed-off-by: Peter Hutterer --- test/pointer.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/test/pointer.c b/test/pointer.c index 557fed7b..0d493103 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -1116,7 +1116,7 @@ START_TEST(middlebutton_default_enabled) struct libinput_device *device = dev->libinput_device; enum libinput_config_status status; int available; - enum libinput_config_middle_emulation_state deflt; + enum libinput_config_middle_emulation_state deflt, state; available = libinput_device_config_middle_emulation_is_available(device); ck_assert(available); @@ -1126,12 +1126,12 @@ START_TEST(middlebutton_default_enabled) else deflt = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; - status = libinput_device_config_middle_emulation_get_enabled(device); - ck_assert_int_eq(status, deflt); + state = libinput_device_config_middle_emulation_get_enabled(device); + ck_assert_int_eq(state, deflt); - status = libinput_device_config_middle_emulation_get_default_enabled( + state = libinput_device_config_middle_emulation_get_default_enabled( device); - ck_assert_int_eq(status, deflt); + ck_assert_int_eq(state, deflt); status = libinput_device_config_middle_emulation_set_enabled(device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); @@ -1151,16 +1151,17 @@ START_TEST(middlebutton_default_clickpad) struct litest_device *dev = litest_current_device(); struct libinput_device *device = dev->libinput_device; enum libinput_config_status status; + enum libinput_config_middle_emulation_state state; int available; available = libinput_device_config_middle_emulation_is_available(device); ck_assert(!available); - status = libinput_device_config_middle_emulation_get_enabled(device); - ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); - status = libinput_device_config_middle_emulation_get_default_enabled( + state = libinput_device_config_middle_emulation_get_enabled(device); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + state = libinput_device_config_middle_emulation_get_default_enabled( device); - ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); status = libinput_device_config_middle_emulation_set_enabled(device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); @@ -1179,7 +1180,7 @@ START_TEST(middlebutton_default_touchpad) { struct litest_device *dev = litest_current_device(); struct libinput_device *device = dev->libinput_device; - enum libinput_config_status status; + enum libinput_config_middle_emulation_state state; int available; available = libinput_device_config_middle_emulation_is_available(device); @@ -1188,12 +1189,12 @@ START_TEST(middlebutton_default_touchpad) if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) return; - status = libinput_device_config_middle_emulation_get_enabled( + state = libinput_device_config_middle_emulation_get_enabled( device); - ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); - status = libinput_device_config_middle_emulation_get_default_enabled( + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + state = libinput_device_config_middle_emulation_get_default_enabled( device); - ck_assert_int_eq(status, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); } END_TEST From fb53e08f967268174a33a5a7772bf313659199e6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 21 Apr 2015 18:24:39 +1000 Subject: [PATCH 054/165] tools: add --set-scroll-button as option Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/Makefile.am | 2 ++ tools/shared.c | 24 ++++++++++++++++++++++++ tools/shared.h | 1 + 3 files changed, 27 insertions(+) diff --git a/tools/Makefile.am b/tools/Makefile.am index b8cc218b..707e952d 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -9,6 +9,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ libshared_la_SOURCES = \ shared.c \ shared.h +libshared_la_CFLAGS = $(LIBEVDEV_CFLAGS) +libshared_la_LIBADD = $(LIBEVDEV_LIBS) event_debug_SOURCES = event-debug.c event_debug_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) diff --git a/tools/shared.c b/tools/shared.c index 15ba8bc5..9ccd5dda 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -30,6 +30,8 @@ #include #include +#include + #include "shared.h" enum options { @@ -47,6 +49,7 @@ enum options { OPT_MIDDLEBUTTON_DISABLE, OPT_CLICK_METHOD, OPT_SCROLL_METHOD, + OPT_SCROLL_BUTTON, OPT_SPEED, }; @@ -78,6 +81,7 @@ tools_usage() "--disable-middlebutton.... enable/disable middle button emulation\n" "--set-click-method=[none|clickfinger|buttonareas] .... set the desired click method\n" "--set-scroll-method=[none|twofinger|edge|button] ... set the desired scroll method\n" + "--set-scroll-button=BTN_MIDDLE ... set the button to the given button code\n" "--set-speed=.... set pointer acceleration speed\n" "\n" "These options apply to all applicable devices, if a feature\n" @@ -99,6 +103,7 @@ tools_init_options(struct tools_options *options) options->middlebutton = -1; options->click_method = -1; options->scroll_method = -1; + options->scroll_button = -1; options->backend = BACKEND_UDEV; options->seat = "seat0"; options->speed = 0.0; @@ -125,6 +130,7 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) { "disable-middlebutton", 0, 0, OPT_MIDDLEBUTTON_DISABLE }, { "set-click-method", 1, 0, OPT_CLICK_METHOD }, { "set-scroll-method", 1, 0, OPT_SCROLL_METHOD }, + { "set-scroll-button", 1, 0, OPT_SCROLL_BUTTON }, { "speed", 1, 0, OPT_SPEED }, { 0, 0, 0, 0} }; @@ -219,6 +225,21 @@ tools_parse_args(int argc, char **argv, struct tools_options *options) return 1; } break; + case OPT_SCROLL_BUTTON: + if (!optarg) { + tools_usage(); + return 1; + } + options->scroll_button = + libevdev_event_code_from_name(EV_KEY, + optarg); + if (options->scroll_button == -1) { + fprintf(stderr, + "Invalid button %s\n", + optarg); + return 1; + } + break; case OPT_SPEED: if (!optarg) { tools_usage(); @@ -346,6 +367,9 @@ tools_device_apply_config(struct libinput_device *device, if (options->scroll_method != -1) libinput_device_config_scroll_set_method(device, options->scroll_method); + if (options->scroll_button != -1) + libinput_device_config_scroll_set_button(device, + options->scroll_button); if (libinput_device_config_accel_is_available(device)) libinput_device_config_accel_set_speed(device, diff --git a/tools/shared.h b/tools/shared.h index 7b037882..a1aec462 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -42,6 +42,7 @@ struct tools_options { int middlebutton; enum libinput_config_click_method click_method; enum libinput_config_scroll_method scroll_method; + int scroll_button; double speed; }; From 7016a960b88d1d1e9bae2baf57dcd21e61932ab7 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 08:16:42 +1000 Subject: [PATCH 055/165] doc: add a note that sometimes there's more than one ID_INPUT_* set Just setting one of them on a device doesn't guarantee that libinput takes that as device type. Signed-off-by: Peter Hutterer --- doc/device-configuration-via-udev.dox | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox index fc1c0af8..c319c409 100644 --- a/doc/device-configuration-via-udev.dox +++ b/doc/device-configuration-via-udev.dox @@ -44,7 +44,10 @@ udev_device_type. ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK, ID_INPUT_ACCELEROMETER
If any of the above is set, libinput initializes the device as the given -type, see @ref udev_device_type.
+type, see @ref udev_device_type. Note that for historical reasons more than +one of these may be set at any time, libinput will select only one of these +to determine the device type. To ensure libinput selects the correct device +type, only set one of them.
WL_SEAT
Assigns the logical seat for this device. See libinput_seat_get_logical_name() From 9df84962fd6eda60447bf37fe0fef9ed8e9b3f19 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 08:20:34 +1000 Subject: [PATCH 056/165] Sort the middle button symbols additions correctly Signed-off-by: Peter Hutterer --- src/libinput.sym | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libinput.sym b/src/libinput.sym index 3c47ee5c..431870f4 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -130,8 +130,8 @@ local: LIBINPUT_0.14.0 { global: + libinput_device_config_middle_emulation_get_default_enabled; + libinput_device_config_middle_emulation_get_enabled; libinput_device_config_middle_emulation_is_available; libinput_device_config_middle_emulation_set_enabled; - libinput_device_config_middle_emulation_get_enabled; - libinput_device_config_middle_emulation_get_default_enabled; } LIBINPUT_0.12.0; From a0cdb7f6227c2894836cc674aa04884b0a09e7dc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 08:24:27 +1000 Subject: [PATCH 057/165] Update to v4.0 kernel header Signed-off-by: Peter Hutterer --- include/linux/input.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/input.h b/include/linux/input.h index e5acbe5b..4bf3d6d4 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -162,6 +162,7 @@ struct input_keymap_entry { #define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ #define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ #define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ +#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */ #define INPUT_PROP_MAX 0x1f #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) @@ -967,7 +968,8 @@ struct input_keymap_entry { */ #define MT_TOOL_FINGER 0 #define MT_TOOL_PEN 1 -#define MT_TOOL_MAX 1 +#define MT_TOOL_PALM 2 +#define MT_TOOL_MAX 2 /* * Values describing the status of a force-feedback effect From 7589d7be037bf36fa9be51e153f2122a2ecf366e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 08:20:56 +1000 Subject: [PATCH 058/165] configure.ac: libinput 0.14.0 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 23354542..9c9f566b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) -m4_define([libinput_minor_version], [13]) +m4_define([libinput_minor_version], [14]) m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=10:1:0 +LIBINPUT_LT_VERSION=11:0:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From 49ad4448b34ff0f40453c307c74289e336653697 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 09:18:34 +1000 Subject: [PATCH 059/165] tools: distribute the new man page Fixes distcheck Signed-off-by: Peter Hutterer --- tools/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index 707e952d..ae3a27f1 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -24,7 +24,7 @@ ptraccel_debug_LDFLAGS = -no-install libinput_list_devices_SOURCES = libinput-list-devices.c libinput_list_devices_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) libinput_list_devices_CFLAGS = $(LIBUDEV_CFLAGS) -man1_MANS = libinput-list-devices.man +dist_man1_MANS = libinput-list-devices.man if BUILD_EVENTGUI noinst_PROGRAMS += event-gui From d4d2abe97110dadc2a45caea6c39a44cf0fb0bde Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 09:19:13 +1000 Subject: [PATCH 060/165] configure.ac: libinput 0.14.1 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 9c9f566b..8ecbf92e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) m4_define([libinput_minor_version], [14]) -m4_define([libinput_micro_version], [0]) +m4_define([libinput_micro_version], [1]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=11:0:1 +LIBINPUT_LT_VERSION=11:1:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From f877ff2f96e79c8572949a7e8ba0f165300880e6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 15:31:21 +1000 Subject: [PATCH 061/165] tools: use doubles to back the scroll bars Slow scrolling otherwise won't move them because we keep truncating the subpixels. Signed-off-by: Peter Hutterer --- tools/event-gui.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/event-gui.c b/tools/event-gui.c index 3ce74183..678e75bc 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -62,8 +62,8 @@ struct window { int absx, absy; /* scroll bar positions */ - int vx, vy; - int hx, hy; + double vx, vy; + double hx, hy; /* touch positions */ struct touch touches[32]; @@ -363,7 +363,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w) LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { value = libinput_event_pointer_get_axis_value(p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); - w->vy += (int)value; + w->vy += value; w->vy = clip(w->vy, 0, w->height); } @@ -371,7 +371,7 @@ handle_event_axis(struct libinput_event *ev, struct window *w) LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) { value = libinput_event_pointer_get_axis_value(p, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); - w->hx += (int)value; + w->hx += value; w->hx = clip(w->hx, 0, w->width); } } From 5c671e0375cc03032fcac26efe4a7835a74c8bfc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 1 Apr 2015 11:03:19 +0200 Subject: [PATCH 062/165] evdev: Add support for POINTINGSTICK_CONST_ACCEL udev property There is quite a wide spread in the delta events generated by trackpoints, some generate deltas of 1-2 under normal use, while others generate deltas from 1-20. It is desirable to normalize trackpoint deltas just like we are normalizing mouse deltas to 1000 dpi, so as to give different model laptops aprox. the same trackpoint cursor speed ootb. Recent versions of udev + hwdb set a POINTINGSTICK_CONST_ACCEL udev property which can be used to adjust trackpoints which are too slow / too fast ootb, this commit implements support for that property. Signed-off-by: Hans de Goede Reviewed-by: Peter Hutterer --- doc/device-configuration-via-udev.dox | 4 ++++ src/evdev.c | 33 +++++++++++++++++++++++++++ src/evdev.h | 7 ++++++ src/libinput-util.c | 31 +++++++++++++++++++++++++ src/libinput-util.h | 1 + test/misc.c | 27 ++++++++++++++++++++++ 6 files changed, 103 insertions(+) diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox index c319c409..c22aafab 100644 --- a/doc/device-configuration-via-udev.dox +++ b/doc/device-configuration-via-udev.dox @@ -60,6 +60,10 @@ See @ref motion_normalization for details.
The angle in degrees for each click on a mouse wheel. See libinput_pointer_get_axis_source() for details.
+
POINTINGSTICK_CONST_ACCEL
+
A constant (linear) acceleration factor to apply to pointingstick deltas +to normalize them. +
Below is an example udev rule to assign "seat1" to a device from vendor diff --git a/src/evdev.c b/src/evdev.c index 1a7282d8..8774a990 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1365,6 +1365,31 @@ evdev_read_wheel_click_prop(struct evdev_device *device) return angle; } + +static inline int +evdev_get_trackpoint_dpi(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + const char *trackpoint_accel; + double accel = DEFAULT_TRACKPOINT_ACCEL; + + trackpoint_accel = udev_device_get_property_value( + device->udev_device, "POINTINGSTICK_CONST_ACCEL"); + if (trackpoint_accel) { + accel = parse_trackpoint_accel_property(trackpoint_accel); + if (accel == 0.0) { + log_error(libinput, "Trackpoint accel property for " + "'%s' is present but invalid, " + "using %.2f instead\n", + device->devname, + DEFAULT_TRACKPOINT_ACCEL); + accel = DEFAULT_TRACKPOINT_ACCEL; + } + } + + return DEFAULT_MOUSE_DPI / accel; +} + static inline int evdev_read_dpi_prop(struct evdev_device *device) { @@ -1372,6 +1397,14 @@ evdev_read_dpi_prop(struct evdev_device *device) const char *mouse_dpi; int dpi = DEFAULT_MOUSE_DPI; + /* + * Trackpoints do not have dpi, instead hwdb may contain a + * POINTINGSTICK_CONST_ACCEL value to compensate for sensitivity + * differences between models, we translate this to a fake dpi. + */ + if (libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK)) + return evdev_get_trackpoint_dpi(device); + mouse_dpi = udev_device_get_property_value(device->udev_device, "MOUSE_DPI"); if (mouse_dpi) { diff --git a/src/evdev.h b/src/evdev.h index 9b1dcc36..9969d515 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -36,6 +36,13 @@ /* The HW DPI rate we normalize to before calculating pointer acceleration */ #define DEFAULT_MOUSE_DPI 1000 + +/* + * The constant (linear) acceleration factor we use to normalize trackpoint + * deltas before calculating pointer acceleration. + */ +#define DEFAULT_TRACKPOINT_ACCEL 1.0 + /* The fake resolution value for abs devices without resolution */ #define EVDEV_FAKE_RESOLUTION 1 diff --git a/src/libinput-util.c b/src/libinput-util.c index 49e297af..4857435b 100644 --- a/src/libinput-util.c +++ b/src/libinput-util.c @@ -29,6 +29,7 @@ #include "config.h" #include +#include #include #include #include @@ -201,3 +202,33 @@ parse_mouse_wheel_click_angle_property(const char *prop) return angle; } + +/** + * Helper function to parse the TRACKPOINT_CONST_ACCEL property from udev. + * Property is of the form: + * TRACKPOINT_CONST_ACCEL= + * + * @param prop The value of the udev property (without the TRACKPOINT_CONST_ACCEL=) + * @return The acceleration, or 0.0 on error. + */ +double +parse_trackpoint_accel_property(const char *prop) +{ + locale_t c_locale; + double accel; + char *endp; + + /* Create a "C" locale to force strtod to use '.' as separator */ + c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); + if (c_locale == (locale_t)0) + return 0.0; + + accel = strtod_l(prop, &endp, c_locale); + + freelocale(c_locale); + + if (*endp != '\0') + return 0.0; + + return accel; +} diff --git a/src/libinput-util.h b/src/libinput-util.h index 30f59ecf..74226b96 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -242,5 +242,6 @@ enum ratelimit_state ratelimit_test(struct ratelimit *r); int parse_mouse_dpi_property(const char *prop); int parse_mouse_wheel_click_angle_property(const char *prop); +double parse_trackpoint_accel_property(const char *prop); #endif /* LIBINPUT_UTIL_H */ diff --git a/test/misc.c b/test/misc.c index db26d67e..829da7d3 100644 --- a/test/misc.c +++ b/test/misc.c @@ -557,6 +557,32 @@ START_TEST(wheel_click_parser) } END_TEST +struct parser_test_float { + char *tag; + double expected_value; +}; + +START_TEST(trackpoint_accel_parser) +{ + struct parser_test_float tests[] = { + { "0.5", 0.5 }, + { "1.0", 1.0 }, + { "2.0", 2.0 }, + { "fail1.0", 0.0 }, + { "1.0fail", 0.0 }, + { "0,5", 0.0 }, + { NULL, 0.0 } + }; + int i; + double accel; + + for (i = 0; tests[i].tag != NULL; i++) { + accel = parse_trackpoint_accel_property(tests[i].tag); + ck_assert(accel == tests[i].expected_value); + } +} +END_TEST + int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); @@ -572,6 +598,7 @@ int main (int argc, char **argv) { litest_add_no_device("misc:ratelimit", ratelimit_helpers); litest_add_no_device("misc:dpi parser", dpi_parser); litest_add_no_device("misc:wheel click parser", wheel_click_parser); + litest_add_no_device("misc:trackpoint accel parser", trackpoint_accel_parser); return litest_run(argc, argv); } From 4996076a7c59690ad02162923b23b763a3b76bcd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 22 Apr 2015 14:36:25 +1000 Subject: [PATCH 063/165] Add libinput_device_keyboard_has_key() Similar to libinput_device_pointer_has_button(), this function returns whether a given device has a specific keycode. This enables a caller to determine if the device is really a keyboard (check for KEY_A-KEY_Z) or just a media key device (check for KEY_PLAY or somesuch), depending on the context required. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Reviewed-by: Derek Foreman --- src/evdev.c | 9 +++++++++ src/evdev.h | 3 +++ src/libinput.c | 6 ++++++ src/libinput.h | 16 ++++++++++++++++ src/libinput.sym | 5 +++++ test/keyboard.c | 20 ++++++++++++++++++++ 6 files changed, 59 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 8774a990..964b3ba1 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2148,6 +2148,15 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code) return libevdev_has_event_code(device->evdev, EV_KEY, code); } +int +evdev_device_has_key(struct evdev_device *device, uint32_t code) +{ + if (!(device->seat_caps & EVDEV_DEVICE_KEYBOARD)) + return -1; + + return libevdev_has_event_code(device->evdev, EV_KEY, code); +} + static inline bool evdev_is_scrolling(const struct evdev_device *device, enum libinput_pointer_axis axis) diff --git a/src/evdev.h b/src/evdev.h index 9969d515..9f244534 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -316,6 +316,9 @@ evdev_device_get_size(struct evdev_device *device, int evdev_device_has_button(struct evdev_device *device, uint32_t code); +int +evdev_device_has_key(struct evdev_device *device, uint32_t code); + double evdev_device_transform_x(struct evdev_device *device, double x, diff --git a/src/libinput.c b/src/libinput.c index 7dcf2965..5ef7edfb 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1547,6 +1547,12 @@ libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code return evdev_device_has_button((struct evdev_device *)device, code); } +LIBINPUT_EXPORT int +libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code) +{ + return evdev_device_has_key((struct evdev_device *)device, code); +} + LIBINPUT_EXPORT struct libinput_event * libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event) { diff --git a/src/libinput.h b/src/libinput.h index b4b373e8..9ef8b8f4 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -1708,6 +1708,22 @@ libinput_device_get_size(struct libinput_device *device, int libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code); +/** + * @ingroup device + * + * Check if a @ref LIBINPUT_DEVICE_CAP_KEYBOARD device has a key with the + * given code (see linux/input.h). + * + * @param device A current input device + * @param code Key code to check for, e.g. KEY_ESC + * + * @return 1 if the device supports this key code, 0 if it does not, -1 + * on error. + */ +int +libinput_device_keyboard_has_key(struct libinput_device *device, + uint32_t code); + /** * @ingroup device * diff --git a/src/libinput.sym b/src/libinput.sym index 431870f4..9c11174b 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -135,3 +135,8 @@ global: libinput_device_config_middle_emulation_is_available; libinput_device_config_middle_emulation_set_enabled; } LIBINPUT_0.12.0; + +LIBINPUT_0.15.0 { +global: + libinput_device_keyboard_has_key; +} LIBINPUT_0.14.0; diff --git a/test/keyboard.c b/test/keyboard.c index cb7ad522..a477cfb7 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -290,12 +290,32 @@ START_TEST(keyboard_key_auto_release) } END_TEST +START_TEST(keyboard_has_key) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + unsigned int code; + int evdev_has, libinput_has; + + ck_assert(libinput_device_has_capability( + device, + LIBINPUT_DEVICE_CAP_KEYBOARD)); + + for (code = 0; code < KEY_CNT; code++) { + evdev_has = libevdev_has_event_code(dev->evdev, EV_KEY, code); + libinput_has = libinput_device_keyboard_has_key(device, code); + ck_assert_int_eq(evdev_has, libinput_has); + } +} +END_TEST + int main(int argc, char **argv) { litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count); litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release); litest_add_no_device("keyboard:key counting", keyboard_key_auto_release); + litest_add("keyboard:keys", keyboard_has_key, LITEST_KEYS, LITEST_ANY); return litest_run(argc, argv); } From b81afb019a78d7a0056eb9afeebf32a5062918dc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 22 Apr 2015 10:32:38 +0200 Subject: [PATCH 064/165] test-touchpad: Adjust touchpad_edge_scroll_no_2fg test for gesture support Unlike all the other 2fg scroll tests the touchpad_edge_scroll_no_2fg test puts the 2 fingers down quite far apart, this makes the pinch vs scroll gesture detection code in the gestures branch detect a pinch causing the test to fail. This commit brings the finger placement in line with the other 2fg scroll tests fixing this. Signed-off-by: Hans de Goede Signed-off-by: Peter Hutterer --- test/touchpad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index f3d2e6a2..c04ef115 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2829,9 +2829,9 @@ START_TEST(touchpad_edge_scroll_no_2fg) litest_drain_events(li); enable_edge_scroll(dev); - litest_touch_down(dev, 0, 20, 20); - litest_touch_down(dev, 1, 40, 20); - litest_touch_move_two_touches(dev, 20, 20, 40, 20, 20, 30, 10, 3); + litest_touch_down(dev, 0, 49, 50); + litest_touch_down(dev, 1, 51, 50); + litest_touch_move_two_touches(dev, 49, 50, 51, 50, 20, 30, 5, 0); libinput_dispatch(li); litest_touch_up(dev, 0); litest_touch_up(dev, 1); From 0515a670afa36ec6e6abe98f68abe1af6eebba81 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Apr 2015 14:29:44 -0400 Subject: [PATCH 065/165] evdev: add support for LIBINPUT_MODEL_* udev tags Some devices need specific configuration or different defaults. Push that into udev rules and a hwdb file, that's where detection is the easiest. The LIBINPUT_MODEL_ prefix is used to determine some type of device model. Note that this property is a private API and subject to change at any time without notice. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede Tested-by: Benjamin Tissoires --- doc/device-configuration-via-udev.dox | 15 +++++++++++++++ src/evdev.c | 22 ++++++++++++++++++++++ src/evdev.h | 6 ++++++ udev/90-libinput-model-quirks.hwdb | 16 ++++++++++++++++ udev/90-libinput-model-quirks.rules | 25 +++++++++++++++++++++++++ udev/Makefile.am | 8 +++++++- 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 udev/90-libinput-model-quirks.hwdb create mode 100644 udev/90-libinput-model-quirks.rules diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox index c22aafab..e38b93f6 100644 --- a/doc/device-configuration-via-udev.dox +++ b/doc/device-configuration-via-udev.dox @@ -63,6 +63,9 @@ libinput_pointer_get_axis_source() for details.
POINTINGSTICK_CONST_ACCEL
A constant (linear) acceleration factor to apply to pointingstick deltas to normalize them. +
LIBINPUT_MODEL_*
+
This prefix is reserved as private API, do not use.. See @ref +model_specific_configuration for details.
@@ -98,4 +101,16 @@ ACTION=="add|change", KERNEL=="event[0-9]*", ENV{ID_VENDOR_ID}=="012a", \ ENV{ID_MODEL_ID}=="034b", ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1" @endcode +@section model_specific_configuration Model-specific configuration + +libinput reserves the property prefix LIBINPUT_MODEL_ for +model-specific configuration. This prefix is reserved as private API, do +not use. + +The effect of this property may be to enable or disable certain +features on a specific device or set of devices, to change configuration +defaults or any other reason. The effects of setting this property, the +format of the property and the value of the property are subject to change +at any time. + */ diff --git a/src/evdev.c b/src/evdev.c index 964b3ba1..e3bba93e 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1422,6 +1422,27 @@ evdev_read_dpi_prop(struct evdev_device *device) return dpi; } +static inline enum evdev_device_model +evdev_read_model(struct evdev_device *device) +{ + const struct model_map { + const char *property; + enum evdev_device_model model; + } model_map[] = { + { NULL, EVDEV_MODEL_DEFAULT }, + }; + const struct model_map *m = model_map; + + while (m->property) { + if (!!udev_device_get_property_value(device->udev_device, + m->property)) + break; + m++; + } + + return m->model; +} + /* Return 1 if the given resolutions have been set, or 0 otherwise */ inline int evdev_fix_abs_resolution(struct evdev_device *device, @@ -1950,6 +1971,7 @@ evdev_device_create(struct libinput_seat *seat, device->scroll.wheel_click_angle = evdev_read_wheel_click_prop(device); device->dpi = evdev_read_dpi_prop(device); + device->model = evdev_read_model(device); /* at most 5 SYN_DROPPED log-messages per 30s */ ratelimit_init(&device->syn_drop_limit, 30ULL * 1000, 5); diff --git a/src/evdev.h b/src/evdev.h index 9f244534..edd40119 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -93,6 +93,10 @@ enum evdev_middlebutton_event { MIDDLEBUTTON_EVENT_ALL_UP, }; +enum evdev_device_model { + EVDEV_MODEL_DEFAULT, +}; + struct mt_slot { int32_t seat_slot; struct device_coords point; @@ -202,6 +206,8 @@ struct evdev_device { int dpi; /* HW resolution */ struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */ + + enum evdev_device_model model; }; #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb new file mode 100644 index 00000000..02bb8f97 --- /dev/null +++ b/udev/90-libinput-model-quirks.hwdb @@ -0,0 +1,16 @@ +# Do not edit this file, it will be overwritten on update +# +# This file contains hwdb matches for libinput model-specific quirks. +# The contents of this file are a contract between libinput, udev rules and +# the hwdb. +# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME + +# The lookup keys are composed in: +# 90-libinput-model-quirks.rules +# +# Match string formats: +# libinput: +# libinput:name::dmi: + +# +# Sort by brand, model diff --git a/udev/90-libinput-model-quirks.rules b/udev/90-libinput-model-quirks.rules new file mode 100644 index 00000000..4b988748 --- /dev/null +++ b/udev/90-libinput-model-quirks.rules @@ -0,0 +1,25 @@ +# Do not edit this file, it will be overwritten on update +# +# This file contains lookup rules for libinput model-specific quirks. +# The contents of this file are a contract between libinput, udev rules and +# the hwdb. +# IT IS NOT A STABLE API AND SUBJECT TO CHANGE AT ANY TIME +# +# The hwdb database is in: +# 90-libinput-model-quirks.hwdb + +ACTION!="add|change", GOTO="libinput_model_quirks_end" +KERNEL!="event*", GOTO="libinput_model_quirks_end" + +# hwdb matches: +# +# libinput: +IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:", \ + GOTO="libinput_model_quirks_end" + +# libinput:name::dmi: +KERNELS=="input*", \ + IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'", \ + GOTO="libinput_model_quirks_end" + +LABEL="libinput_model_quirks_end" diff --git a/udev/Makefile.am b/udev/Makefile.am index 3691172c..7d19809d 100644 --- a/udev/Makefile.am +++ b/udev/Makefile.am @@ -6,4 +6,10 @@ libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS) libinput_device_group_LDADD = $(LIBUDEV_LIBS) udev_rulesdir=$(UDEV_DIR)/rules.d -dist_udev_rules_DATA = 80-libinput-device-groups.rules +dist_udev_rules_DATA = \ + 80-libinput-device-groups.rules \ + 90-libinput-model-quirks.rules + +udev_hwdbdir=$(UDEV_DIR)/hwdb.d +dist_udev_hwdb_DATA = \ + 90-libinput-model-quirks.hwdb From d4e6692d03bddf71f2a7b371790b5198dc962a98 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 12:02:28 +1000 Subject: [PATCH 066/165] touchpad: indentation and other whitespace fixes Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 61 +++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index f64343cd..2869be3c 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -144,12 +144,15 @@ tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t) * as described in the state machine diagram. */ static void -tp_button_set_state(struct tp_dispatch *tp, struct tp_touch *t, - enum button_state new_state, enum button_event event) +tp_button_set_state(struct tp_dispatch *tp, + struct tp_touch *t, + enum button_state new_state, + enum button_event event) { libinput_timer_cancel(&t->button.timer); t->button.state = new_state; + switch (t->button.state) { case BUTTON_STATE_NONE: t->button.curr = 0; @@ -235,7 +238,9 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp, case BUTTON_EVENT_IN_BOTTOM_R: case BUTTON_EVENT_IN_BOTTOM_L: if (event != t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, + tp_button_set_state(tp, + t, + BUTTON_STATE_BOTTOM, event); break; case BUTTON_EVENT_IN_TOP_R: @@ -256,8 +261,8 @@ tp_button_bottom_handle_event(struct tp_dispatch *tp, static void tp_button_top_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) + struct tp_touch *t, + enum button_event event) { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: @@ -268,7 +273,9 @@ tp_button_top_handle_event(struct tp_dispatch *tp, case BUTTON_EVENT_IN_TOP_M: case BUTTON_EVENT_IN_TOP_L: if (event != t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, + tp_button_set_state(tp, + t, + BUTTON_STATE_TOP_NEW, event); break; case BUTTON_EVENT_IN_AREA: @@ -286,8 +293,8 @@ tp_button_top_handle_event(struct tp_dispatch *tp, static void tp_button_top_new_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) + struct tp_touch *t, + enum button_event event) { switch(event) { case BUTTON_EVENT_IN_BOTTOM_R: @@ -298,7 +305,9 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp, case BUTTON_EVENT_IN_TOP_M: case BUTTON_EVENT_IN_TOP_L: if (event != t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, + tp_button_set_state(tp, + t, + BUTTON_STATE_TOP_NEW, event); break; case BUTTON_EVENT_IN_AREA: @@ -320,18 +329,22 @@ tp_button_top_new_handle_event(struct tp_dispatch *tp, static void tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) + struct tp_touch *t, + enum button_event event) { switch(event) { case BUTTON_EVENT_IN_TOP_R: case BUTTON_EVENT_IN_TOP_M: case BUTTON_EVENT_IN_TOP_L: if (event == t->button.curr) - tp_button_set_state(tp, t, BUTTON_STATE_TOP, + tp_button_set_state(tp, + t, + BUTTON_STATE_TOP, event); else - tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, + tp_button_set_state(tp, + t, + BUTTON_STATE_TOP_NEW, event); break; case BUTTON_EVENT_IN_BOTTOM_R: @@ -352,8 +365,8 @@ tp_button_top_to_ignore_handle_event(struct tp_dispatch *tp, static void tp_button_ignore_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum button_event event) + struct tp_touch *t, + enum button_event event) { switch (event) { case BUTTON_EVENT_IN_BOTTOM_R: @@ -769,8 +782,10 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp, event.type = EV_KEY; event.code = button; event.value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0; - dispatch->interface->process(dispatch, tp->buttons.trackpoint, - &event, time); + dispatch->interface->process(dispatch, + tp->buttons.trackpoint, + &event, + time); return 1; } @@ -783,7 +798,7 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp, * by the softbutton code with one based on the number of fingers. */ if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && - state == LIBINPUT_BUTTON_STATE_PRESSED) { + state == LIBINPUT_BUTTON_STATE_PRESSED) { switch (tp->nfingers_down) { case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; @@ -874,8 +889,11 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) tp->buttons.click_pending = false; if (button) - return tp_notify_clickpadbutton(tp, time, button, is_top, state); - + return tp_notify_clickpadbutton(tp, + time, + button, + is_top, + state); return 0; } @@ -897,5 +915,6 @@ tp_button_touch_active(struct tp_dispatch *tp, struct tp_touch *t) bool tp_button_is_inside_softbutton_area(struct tp_dispatch *tp, struct tp_touch *t) { - return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t); + return is_inside_top_button_area(tp, t) || + is_inside_bottom_button_area(tp, t); } From aa028b726966fab117c64afd5d5efa861c2d4666 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 12:23:45 +1000 Subject: [PATCH 067/165] touchpad: don't re-use button variable Split into button and area, the latter of which is the bitmask of which area we're in. No functional changes. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 2869be3c..be6dc7ca 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -833,44 +833,45 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) if (current) { struct tp_touch *t; + uint32_t area = 0; tp_for_each_touch(tp, t) { switch (t->button.curr) { case BUTTON_EVENT_IN_AREA: - button |= AREA; + area |= AREA; break; case BUTTON_EVENT_IN_TOP_L: is_top = 1; /* fallthrough */ case BUTTON_EVENT_IN_BOTTOM_L: - button |= LEFT; + area |= LEFT; break; case BUTTON_EVENT_IN_TOP_M: is_top = 1; - button |= MIDDLE; + area |= MIDDLE; break; case BUTTON_EVENT_IN_TOP_R: is_top = 1; /* fallthrough */ case BUTTON_EVENT_IN_BOTTOM_R: - button |= RIGHT; + area |= RIGHT; break; default: break; } } - if (button == 0) { + if (area == 0) { /* No touches, wait for a touch before processing */ tp->buttons.click_pending = true; return 0; } - if ((button & MIDDLE) || ((button & LEFT) && (button & RIGHT))) + if ((area & MIDDLE) || ((area & LEFT) && (area & RIGHT))) button = evdev_to_left_handed(tp->device, BTN_MIDDLE); - else if (button & RIGHT) + else if (area & RIGHT) button = evdev_to_left_handed(tp->device, BTN_RIGHT); - else if (button & LEFT) + else if (area & LEFT) button = evdev_to_left_handed(tp->device, BTN_LEFT); else /* main area is always BTN_LEFT */ button = BTN_LEFT; From a310a5f6230840483678986ee8645dbdea150dd0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 12:36:43 +1000 Subject: [PATCH 068/165] touchpad: minor code cleanup No functional changes, just reducing to one instance of the call with a variable parameter. Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index be6dc7ca..c907ecc5 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -439,18 +439,22 @@ tp_button_handle_state(struct tp_dispatch *tp, uint64_t time) if (t->state == TOUCH_END) { tp_button_handle_event(tp, t, BUTTON_EVENT_UP, time); } else if (t->dirty) { + enum button_event event; + if (is_inside_bottom_right_area(tp, t)) - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_R, time); + event = BUTTON_EVENT_IN_BOTTOM_R; else if (is_inside_bottom_left_area(tp, t)) - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_BOTTOM_L, time); + event = BUTTON_EVENT_IN_BOTTOM_L; else if (is_inside_top_right_area(tp, t)) - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_R, time); + event = BUTTON_EVENT_IN_TOP_R; else if (is_inside_top_middle_area(tp, t)) - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_M, time); + event = BUTTON_EVENT_IN_TOP_M; else if (is_inside_top_left_area(tp, t)) - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_TOP_L, time); + event = BUTTON_EVENT_IN_TOP_L; else - tp_button_handle_event(tp, t, BUTTON_EVENT_IN_AREA, time); + event = BUTTON_EVENT_IN_AREA; + + tp_button_handle_event(tp, t, event, time); } if (tp->queued & TOUCHPAD_EVENT_BUTTON_RELEASE) tp_button_handle_event(tp, t, BUTTON_EVENT_RELEASE, time); From c14d7063b75cd68e18e3210137f2194e03b1088a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 12:25:50 +1000 Subject: [PATCH 069/165] touchpad: allow BTN_LEFT in clickfinger mode without touches On the Logitech T650 it's quite easy to trigger a click without touching the surface. For software buttons we discard those clicks because we can't tell where the finger is to decide on left vs right click. It takes effort to trigger a click with two fingers without triggering a touch though, so in clickfinger mode post a click without touches as single-finger click. https://bugs.freedesktop.org/show_bug.cgi?id=90150 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 6 ++++-- test/touchpad.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index c907ecc5..7f60a53a 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -804,6 +804,7 @@ tp_notify_clickpadbutton(struct tp_dispatch *tp, if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER && state == LIBINPUT_BUTTON_STATE_PRESSED) { switch (tp->nfingers_down) { + case 0: case 1: button = BTN_LEFT; break; case 2: button = BTN_RIGHT; break; case 3: button = BTN_MIDDLE; break; @@ -865,7 +866,8 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) } } - if (area == 0) { + if (area == 0 && + tp->buttons.click_method != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) { /* No touches, wait for a touch before processing */ tp->buttons.click_pending = true; return 0; @@ -877,7 +879,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time) button = evdev_to_left_handed(tp->device, BTN_RIGHT); else if (area & LEFT) button = evdev_to_left_handed(tp->device, BTN_LEFT); - else /* main area is always BTN_LEFT */ + else /* main or no area (for clickfinger) is always BTN_LEFT */ button = BTN_LEFT; tp->buttons.active = button; diff --git a/test/touchpad.c b/test/touchpad.c index c04ef115..db981dcb 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -1506,6 +1506,32 @@ START_TEST(touchpad_1fg_clickfinger) } END_TEST +START_TEST(touchpad_1fg_clickfinger_no_touch) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + enum libinput_config_status status; + + status = libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + litest_event(dev, EV_KEY, BTN_LEFT, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + litest_event(dev, EV_KEY, BTN_LEFT, 0); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); +} +END_TEST + START_TEST(touchpad_2fg_clickfinger) { struct litest_device *dev = litest_current_device(); @@ -1785,6 +1811,9 @@ START_TEST(clickpad_btn_left) struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; + libinput_device_config_click_set_method(dev->libinput_device, + LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); + litest_drain_events(li); /* A clickpad always needs a finger down to tell where the @@ -4119,6 +4148,7 @@ int main(int argc, char **argv) { litest_add("touchpad:tap", clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:clickfinger", From fefc31806efd7fb045e66449ae2264bea8fd0f7a Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 24 Apr 2015 17:21:18 -0500 Subject: [PATCH 070/165] test: Add negative test for libinput_device_keyboard_has_key() Ensure we get a -1 return for non-keyboard devices. Signed-off-by: Derek Foreman Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/keyboard.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/keyboard.c b/test/keyboard.c index a477cfb7..508362bb 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -309,6 +309,24 @@ START_TEST(keyboard_has_key) } END_TEST +START_TEST(keyboard_keys_bad_device) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + unsigned int code; + int has_key; + + if (libinput_device_has_capability(device, + LIBINPUT_DEVICE_CAP_KEYBOARD)) + return; + + for (code = 0; code < KEY_CNT; code++) { + has_key = libinput_device_keyboard_has_key(device, code); + ck_assert_int_eq(has_key, -1); + } +} +END_TEST + int main(int argc, char **argv) { @@ -316,6 +334,7 @@ main(int argc, char **argv) litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release); litest_add_no_device("keyboard:key counting", keyboard_key_auto_release); litest_add("keyboard:keys", keyboard_has_key, LITEST_KEYS, LITEST_ANY); + litest_add("keyboard:keys", keyboard_keys_bad_device, LITEST_ANY, LITEST_ANY); return litest_run(argc, argv); } From 06b4d83c4aa96570cc31abc6c299e8b14e015885 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 24 Apr 2015 17:23:13 -0500 Subject: [PATCH 071/165] test: add LITEST_KEYS to roccat mouse features The mouse can generate key events, so it should be carry that feature bit. Signed-off-by: Derek Foreman Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/litest-mouse-roccat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest-mouse-roccat.c b/test/litest-mouse-roccat.c index adccd3d1..115e970d 100644 --- a/test/litest-mouse-roccat.c +++ b/test/litest-mouse-roccat.c @@ -193,7 +193,7 @@ static struct input_absinfo absinfo[] = { struct litest_test_device litest_mouse_roccat_device = { .type = LITEST_MOUSE_ROCCAT, - .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL, + .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_KEYS, .shortname = "mouse_roccat", .setup = litest_mouse_roccat_setup, .interface = NULL, From 26ceea3e3b6dbd83d175ae3b4f862d0792af812c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 23 Apr 2015 14:32:40 -0400 Subject: [PATCH 072/165] evdev: use a different filter for low resolution touchpad on the Lenovo X230 Those touchpads presents an actual lower resolution that what is advertised. We see some jumps from the cursor due to the big steps in X and Y when we are receiving data. For instance, we receive: E: 13.471932 0003 0000 16366 # EV_ABS / ABS_X 16366 E: 13.471932 0003 0001 9591 # EV_ABS / ABS_Y 9591 E: 13.471932 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 13.479924 0003 0000 16316 # EV_ABS / ABS_X 16316 E: 13.479924 0003 0001 9491 # EV_ABS / ABS_Y 9491 E: 13.479924 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- E: 13.487939 0003 0000 16271 # EV_ABS / ABS_X 16271 E: 13.487939 0003 0001 9403 # EV_ABS / ABS_Y 9403 E: 13.487939 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- -> jumps of ~50 in X in each report, and ~100 for Y. Apply a factor to minimize those jumps at low speed, and try keeping the same feeling as regular touchpads at high speed. It still feels slower but it is usable at least Signed-off-by: Benjamin Tissoires Reviewed-by: Peter Hutterer Tested-by: Vasily Khoruzhick Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 14 ++++++++--- src/evdev.c | 1 + src/evdev.h | 1 + src/filter.c | 37 ++++++++++++++++++++++++++++++ src/filter.h | 5 ++++ udev/90-libinput-model-quirks.hwdb | 8 +++++++ 6 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index cccf033d..d5ce8803 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -998,6 +998,7 @@ static int tp_init_accel(struct tp_dispatch *tp, double diagonal) { int res_x, res_y; + accel_profile_func_t profile; res_x = tp->device->abs.absinfo_x->resolution; res_y = tp->device->abs.absinfo_y->resolution; @@ -1021,9 +1022,16 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal; } - if (evdev_device_init_pointer_acceleration( - tp->device, - touchpad_accel_profile_linear) == -1) + switch (tp->device->model) { + case EVDEV_MODEL_LENOVO_X230: + profile = touchpad_lenovo_x230_accel_profile; + break; + default: + profile = touchpad_accel_profile_linear; + break; + } + + if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1) return -1; return 0; diff --git a/src/evdev.c b/src/evdev.c index e3bba93e..6a9df499 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1429,6 +1429,7 @@ evdev_read_model(struct evdev_device *device) const char *property; enum evdev_device_model model; } model_map[] = { + { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index edd40119..151d1031 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -95,6 +95,7 @@ enum evdev_middlebutton_event { enum evdev_device_model { EVDEV_MODEL_DEFAULT, + EVDEV_MODEL_LENOVO_X230, }; struct mt_slot { diff --git a/src/filter.c b/src/filter.c index 962d74de..b953bee8 100644 --- a/src/filter.c +++ b/src/filter.c @@ -345,3 +345,40 @@ touchpad_accel_profile_linear(struct motion_filter *filter, return speed_out * TP_MAGIC_SLOWDOWN; } + +double +touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, + uint64_t time) +{ + /* Keep the magic factor from touchpad_accel_profile_linear. */ + const double TP_MAGIC_SLOWDOWN = 0.4; + + /* Those touchpads presents an actual lower resolution that what is + * advertised. We see some jumps from the cursor due to the big steps + * in X and Y when we are receiving data. + * Apply a factor to minimize those jumps at low speed, and try + * keeping the same feeling as regular touchpads at high speed. + * It still feels slower but it is usable at least */ + const double TP_MAGIC_LOW_RES_FACTOR = 4.0; + double speed_out; + struct pointer_accelerator *accel_filter = + (struct pointer_accelerator *)filter; + + double s1, s2; + const double max_accel = accel_filter->accel * + TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */ + const double threshold = accel_filter->threshold / + TP_MAGIC_LOW_RES_FACTOR; /* units/ms */ + const double incline = accel_filter->incline * TP_MAGIC_LOW_RES_FACTOR; + + speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; + + s1 = min(1, speed_in * 5); + s2 = 1 + (speed_in - threshold) * incline; + + speed_out = min(max_accel, s2 > 1 ? s2 : s1); + + return speed_out * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; +} diff --git a/src/filter.h b/src/filter.h index 70363a62..a0538601 100644 --- a/src/filter.h +++ b/src/filter.h @@ -67,4 +67,9 @@ touchpad_accel_profile_linear(struct motion_filter *filter, void *data, double speed_in, uint64_t time); +double +touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, + uint64_t time); #endif /* FILTER_H */ diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 02bb8f97..959be0ff 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -14,3 +14,11 @@ # # Sort by brand, model + +########################################## +# LENOVO +########################################## + +# X230 (Tablet) +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230* + LIBINPUT_MODEL_LENOVO_X230=1 From 07b20dcce6379bf21dc578858bce78026c25d729 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Apr 2015 16:49:36 +0200 Subject: [PATCH 073/165] touchpad: Only use slot 0 deltas for 2fg scrolling on semi-mt touchpads Some semi-mt model touchpads have a better accuracy for slot 0 then for slot 1 (they only have 2), so on semi-mt models only use the movement of the touch in slot 0 for 2fg scrolling, rather then the average movement of the 2 touches. This fixes 2fg scrolling being choppy / jumpy in some cases. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=89683 Signed-off-by: Hans de Goede Also needed: change the touchpad scroll source test to use more separate events. The current litest semi-mt touch implementation only moves the first touch on every second move. With 5 movements this isn't enough to fill the motion history and generate events, so double it to 10 so we're guaranteed to get scroll events. Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 12 +++++++++++- test/touchpad.c | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index d332186b..57c07feb 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -113,7 +113,17 @@ tp_gesture_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time) if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG) return; - delta = tp_get_average_touches_delta(tp); + /* On some semi-mt models slot 0 is more accurate, so for semi-mt + * we only use slot 0. */ + if (tp->semi_mt) { + if (!tp->touches[0].dirty) + return; + + delta = tp_get_delta(&tp->touches[0]); + } else { + delta = tp_get_average_touches_delta(tp); + } + delta = tp_filter_motion(tp, &delta, time); if (normalized_is_zero(delta)) diff --git a/test/touchpad.c b/test/touchpad.c index db981dcb..35842689 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2464,7 +2464,7 @@ test_2fg_scroll(struct litest_device *dev, double dx, double dy, int want_sleep) litest_touch_down(dev, 0, 49, 50); litest_touch_down(dev, 1, 51, 50); - litest_touch_move_two_touches(dev, 49, 50, 51, 50, dx, dy, 5, 0); + litest_touch_move_two_touches(dev, 49, 50, 51, 50, dx, dy, 10, 0); /* Avoid a small scroll being seen as a tap */ if (want_sleep) { From 5254977fce4262dd89751c644b3ffd190be6d618 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 27 Apr 2015 11:00:24 +1000 Subject: [PATCH 074/165] test: add semi-mt 2fg scroll test Signed-off-by: Peter Hutterer --- test/touchpad.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/touchpad.c b/test/touchpad.c index 35842689..99df57d0 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2579,6 +2579,26 @@ START_TEST(touchpad_2fg_scroll_source) } END_TEST +START_TEST(touchpad_2fg_scroll_semi_mt) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + litest_touch_down(dev, 0, 20, 20); + litest_touch_down(dev, 1, 30, 20); + libinput_dispatch(li); + litest_touch_move_to(dev, 1, 30, 20, 30, 70, 10, 5); + + litest_assert_empty_queue(li); + + litest_touch_move_to(dev, 0, 20, 20, 20, 70, 10, 5); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS); +} +END_TEST + START_TEST(touchpad_2fg_scroll_return_to_motion) { struct litest_device *dev = litest_current_device(); @@ -4185,6 +4205,7 @@ int main(int argc, char **argv) { litest_add("touchpad:scroll", touchpad_2fg_scroll_slow_distance, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:scroll", touchpad_2fg_scroll_return_to_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:scroll", touchpad_2fg_scroll_source, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("touchpad:scroll", touchpad_2fg_scroll_semi_mt, LITEST_SEMI_MT, LITEST_SINGLE_TOUCH); litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:scroll", touchpad_scroll_natural, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); From 852e91bffd33117905bbb8c6ce38a2ea0432495e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 27 Apr 2015 08:56:50 +1000 Subject: [PATCH 075/165] test: fix a memory leak in the touch test Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/touch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/touch.c b/test/touch.c index ac97c52b..5c363f9e 100644 --- a/test/touch.c +++ b/test/touch.c @@ -439,6 +439,8 @@ START_TEST(fake_mt_exists) * have different capabilities */ ck_assert(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)); + + libinput_event_destroy(event); } END_TEST From da439476b529915e9b139135c42d522e18acbc51 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:24:11 +1000 Subject: [PATCH 076/165] test: check for event as not null before dereferencing it Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/pointer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pointer.c b/test/pointer.c index 0d493103..01ea2fdb 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -136,6 +136,7 @@ test_absolute_event(struct litest_device *dev, double x, double y) libinput_dispatch(li); event = libinput_get_event(li); + ck_assert_notnull(event); ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); From f29060b5c764176f1c705ad119226bf3bbad23e5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:12:09 +1000 Subject: [PATCH 077/165] test: drop REP_DELAY from the ms surface cover event codes litest doesn't know how to set this up and we don't need it anyway. Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/litest-ms-surface-cover.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/litest-ms-surface-cover.c b/test/litest-ms-surface-cover.c index 5b9ec233..3e026e00 100644 --- a/test/litest-ms-surface-cover.c +++ b/test/litest-ms-surface-cover.c @@ -368,7 +368,6 @@ static int events[] = { EV_LED, LED_NUML, EV_LED, LED_CAPSL, EV_LED, LED_SCROLLL, - EV_REP, REP_DELAY, -1, -1, }; From af10921550d0cde9cdbc9a50cda91dd0ec7110ac Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:18:17 +1000 Subject: [PATCH 078/165] test: fix features for the MS Surface Cover LITEST_KEYBOARD is the type, LITEST_KEYS is the feature. And this device has buttons, so mark it as such. Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/litest-ms-surface-cover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest-ms-surface-cover.c b/test/litest-ms-surface-cover.c index 3e026e00..633bb790 100644 --- a/test/litest-ms-surface-cover.c +++ b/test/litest-ms-surface-cover.c @@ -373,7 +373,7 @@ static int events[] = { struct litest_test_device litest_ms_surface_cover_device = { .type = LITEST_MS_SURFACE_COVER, - .features = LITEST_KEYBOARD | LITEST_RELATIVE | LITEST_FAKE_MT, + .features = LITEST_KEYS | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL, .shortname = "MS surface cover", .setup = litest_ms_surface_cover_setup, .interface = &interface, From 9191010ac2fc2afc47c78aade6d8a18f062faa1f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:07:58 +1000 Subject: [PATCH 079/165] test: add the MS Surface Type Cover to the list of devices Added in d2842893a828407e3c4d2459a382f8c7179b2f51 but never added to the list of devices so none of the tests ran against it. Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/litest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/litest.c b/test/litest.c index 235ccb9a..6d17a375 100644 --- a/test/litest.c +++ b/test/litest.c @@ -101,6 +101,7 @@ extern struct litest_test_device litest_wacom_finger_device; extern struct litest_test_device litest_keyboard_blackwidow_device; extern struct litest_test_device litest_wheel_only_device; extern struct litest_test_device litest_mouse_roccat_device; +extern struct litest_test_device litest_ms_surface_cover_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -123,6 +124,7 @@ struct litest_test_device* devices[] = { &litest_keyboard_blackwidow_device, &litest_wheel_only_device, &litest_mouse_roccat_device, + &litest_ms_surface_cover_device, NULL, }; From 6083fafe53267c8f0d6120360449eacf220ab078 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:01:55 +1000 Subject: [PATCH 080/165] evdev: reject devices with only REL_X but no REL_Y (or vice versa) Not sure if any exists, if they do let's see if a user files a bug report first so we know what to do with those (they're most likely buttonsets). Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- src/evdev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 6a9df499..db19ac89 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1606,6 +1606,10 @@ evdev_reject_device(struct evdev_device *device) libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) return -1; + if (libevdev_has_event_code(evdev, EV_REL, REL_X) ^ + libevdev_has_event_code(evdev, EV_REL, REL_Y)) + return -1; + if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ^ libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) return -1; From 5e85957ad20f0a084a538745917547aa96b20f96 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 14:59:18 +1000 Subject: [PATCH 081/165] evdev: init pointer accel filters when we have rel x/y axes Some devices provide abs x/y and rel x/y. We can't know which event the device will send. The Microsoft Surface Type Cover sends relative events, which then crashes libinput when we don't have an accel filter set up. So instead of checking that the device doesn't have ABS_X/Y, check for the device to have REL_X/Y instead. https://bugzilla.redhat.com/show_bug.cgi?id=1206869 Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- src/evdev.c | 4 ++-- test/pointer.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index db19ac89..d65b1131 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1792,8 +1792,8 @@ evdev_configure_device(struct evdev_device *device) } if (udev_tags & EVDEV_UDEV_TAG_MOUSE) { - if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) && - !libevdev_has_event_code(evdev, EV_ABS, ABS_Y) && + if (libevdev_has_event_code(evdev, EV_REL, REL_X) && + libevdev_has_event_code(evdev, EV_REL, REL_Y) && evdev_device_init_pointer_acceleration( device, pointer_accel_profile_linear) == -1) diff --git a/test/pointer.c b/test/pointer.c index 01ea2fdb..9d3dda60 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -825,6 +825,17 @@ START_TEST(pointer_accel_defaults_absolute) } END_TEST +START_TEST(pointer_accel_defaults_absolute_relative) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + + ck_assert(libinput_device_config_accel_is_available(device)); + ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0); + ck_assert(libinput_device_config_accel_get_speed(device) == 0.0); +} +END_TEST + START_TEST(pointer_accel_direction_change) { struct litest_device *dev = litest_current_device(); @@ -1223,7 +1234,8 @@ int main (int argc, char **argv) { litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY); - litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY); + litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_RELATIVE); + litest_add("pointer:accel", pointer_accel_defaults_absolute_relative, LITEST_ABSOLUTE|LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_POINTINGSTICK); From e0f467e70dfd40d788df0f99478662e21f2d472a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 24 Apr 2015 15:27:34 +1000 Subject: [PATCH 082/165] test: upgrade to the Microsoft Surface Type Cover Almost identical to the one we already had but this one also has ABS_X/Y to mess things up. Update the existing one, no need to add a separate device here. The fake MT touch test needs to be updated now though. A fake MT device may be an absolute device too, so if we use the touch_down() handlers we may generate abs pointer events. That's valid, we only check for no touch events here. Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- test/litest-ms-surface-cover.c | 10 +++++++--- test/touch.c | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/litest-ms-surface-cover.c b/test/litest-ms-surface-cover.c index 633bb790..37fe850c 100644 --- a/test/litest-ms-surface-cover.c +++ b/test/litest-ms-surface-cover.c @@ -35,6 +35,8 @@ litest_ms_surface_cover_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_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 }, @@ -59,6 +61,8 @@ static struct litest_device_interface interface = { }; static struct input_absinfo absinfo[] = { + { ABS_X, 0, 1022, 0, 0, 12 }, + { ABS_Y, 0, 487, 0, 0, 12 }, { ABS_VOLUME, 0, 1023, 0, 0, 0 }, { ABS_MISC, 0, 255, 0, 0, 0 }, { 41, 0, 255, 0, 0, 0 }, @@ -90,7 +94,7 @@ static struct input_absinfo absinfo[] = { static struct input_id input_id = { .bustype = 0x3, .vendor = 0x45e, - .product = 0x7a9, + .product = 0x7dc, }; static int events[] = { @@ -373,12 +377,12 @@ static int events[] = { struct litest_test_device litest_ms_surface_cover_device = { .type = LITEST_MS_SURFACE_COVER, - .features = LITEST_KEYS | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL, + .features = LITEST_KEYS | LITEST_ABSOLUTE | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL, .shortname = "MS surface cover", .setup = litest_ms_surface_cover_setup, .interface = &interface, - .name = "MICROSOFT SAM", + .name = "Microsoft Surface Type Cover", .id = &input_id, .events = events, .absinfo = absinfo, diff --git a/test/touch.c b/test/touch.c index 5c363f9e..eb9f4cb6 100644 --- a/test/touch.c +++ b/test/touch.c @@ -462,7 +462,8 @@ START_TEST(fake_mt_no_touch_events) litest_touch_up(dev, 0); litest_touch_up(dev, 1); - litest_assert_empty_queue(li); + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); } END_TEST From fe4e707561a00ef77b8465b3c2b470b57bb22d61 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 10:43:55 +1000 Subject: [PATCH 083/165] doc: expand scrolling documentation Signed-off-by: Peter Hutterer --- doc/scrolling.dox | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/doc/scrolling.dox b/doc/scrolling.dox index 3ded909d..b5a01cf8 100644 --- a/doc/scrolling.dox +++ b/doc/scrolling.dox @@ -1,7 +1,20 @@ /** @page scrolling Scrolling -libinput supports three different types of scrolling behavior. +libinput supports three different types of scrolling methods: @ref +twofinger_scrolling, @ref edge_scrolling and @ref button_scrolling. Some devices +support multiple methods, though only one can be enabled at a time. See +libinput_device_config_scroll_set_method() for documentation on how to +switch methods and libinput_device_config_scroll_get_methods() for +documentation on how to query a device for available scroll methods. + +Scroll movements provide vertical and horizontal directions, each +scroll event contains both directions where applicable, see +libinput_event_pointer_get_axis_value(). libinput does not provide separate +toggles to enable or disable horizontal scrolling. Instead, horizontal +scrolling is always enabled. This is intentional, libinput does not have +enough context to know when horizontal scrolling is appropriate for a given +widget. The task of filtering horizontal movements is up to the caller. @section twofinger_scrolling Two-finger scrolling @@ -16,7 +29,6 @@ For scrolling to trigger, a built-in distance threshold has to be met but once engaged any movement will scroll. In other words, to start scrolling a sufficiently large movement is required, once scrolling tiny amounts of movements will translate into tiny scroll movements. - Scrolling in both directions at once is possible by meeting the required distance thresholds to enable each direction separately. @@ -28,23 +40,27 @@ scroll). @image html edge-scrolling.svg "Vertical and horizontal edge scrolling" -Due to the layout of the edges, diagonal scrolling is not possible. +Due to the layout of the edges, diagonal scrolling is not possible. The +behavior of edge scrolling using both edges at the same time is undefined. Edge scrolling conflicts with @ref clickpad_softbuttons and is -not usually available on clickpads. +not usually available on clickpads. See +http://who-t.blogspot.com.au/2015/03/why-libinput-doesnt-support-edge.html +for details. @section button_scrolling On-Button scrolling -Scrolling when a button is held down is available on selected devices. The -motion of a device is converted into scrolling motion. - -For example, Lenovo devices provide a +On-button scrolling converts the motion of a device into scroll events while +a designated button is held down. For example, Lenovo devices provide a pointing stick that emulates scroll events when the trackstick's middle mouse button is held down. @image html button-scrolling.svg "Button scrolling" -Note that libinput's @ref t440_support enables the use of the middle +The button may be changed with +libinput_device_config_scroll_set_button() but must be on the same device as +the motion events. Cross-device scrolling is not supported but +for one exception: libinput's @ref t440_support enables the use of the middle button for button scrolling (even when the touchpad is disabled). */ From 1352fe04328f9313e35f301f82d06605dca673a8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 11:53:11 +1000 Subject: [PATCH 084/165] test: fix touch up for multitap-n-drag test Released the wrong touch point, causing warnings: libevdev error in sanitize_event: BUG: Device "litest SynPS/2 Synaptics TouchPad" received a double tracking ID 6 in slot 0. Signed-off-by: Peter Hutterer --- test/touchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/touchpad.c b/test/touchpad.c index 99df57d0..830e5890 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -430,7 +430,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); - litest_touch_up(dev, 1); + litest_touch_up(dev, 0); litest_timeout_tap(); litest_assert_empty_queue(li); From 106e78cf8a77b9759c9700bab81646a268056bed Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 16:43:01 +1000 Subject: [PATCH 085/165] test: change the suite name for two of the pointer button tests No need for a separate test suite group here. Signed-off-by: Peter Hutterer --- test/pointer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pointer.c b/test/pointer.c index 9d3dda60..b3fb96e6 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -1216,13 +1216,13 @@ int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD); - litest_add_no_device("pointer:button_auto_release", pointer_button_auto_release); + litest_add_no_device("pointer:button", pointer_button_auto_release); + litest_add_no_device("pointer:button", pointer_seat_button_count); litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_natural_defaults, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_natural_wheel, LITEST_WHEEL, LITEST_ANY); - litest_add_no_device("pointer:seat button count", pointer_seat_button_count); litest_add("pointer:calibration", pointer_no_calibration, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH|LITEST_ABSOLUTE|LITEST_PROTOCOL_A); From 512a00505ad45cf93a84522e132327a5b0dbc221 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 16:40:29 +1000 Subject: [PATCH 086/165] test: expand the middle button emulation tests Instead of excluding the pointing stick devices, disable middle button scrolling on those and run them anyway. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/pointer.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/test/pointer.c b/test/pointer.c index b3fb96e6..26d9930c 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -106,6 +106,18 @@ test_relative_event(struct litest_device *dev, int dx, int dy) litest_drain_events(dev->libinput); } +static void +disable_button_scrolling(struct litest_device *device) +{ + struct libinput_device *dev = device->libinput_device; + enum libinput_config_status status; + + status = libinput_device_config_scroll_set_method(dev, + LIBINPUT_CONFIG_SCROLL_NO_SCROLL); + + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); +} + START_TEST(pointer_motion_relative) { struct litest_device *dev = litest_current_device(); @@ -231,6 +243,8 @@ START_TEST(pointer_button) { struct litest_device *dev = litest_current_device(); + disable_button_scrolling(dev); + litest_drain_events(dev->libinput); test_button_event(dev, BTN_LEFT, 1); @@ -246,8 +260,7 @@ START_TEST(pointer_button) } /* Skip middle button test on trackpoints (used for scrolling) */ - if (!libevdev_has_property(dev->evdev, INPUT_PROP_POINTING_STICK) && - libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { + if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) { test_button_event(dev, BTN_MIDDLE, 1); test_button_event(dev, BTN_MIDDLE, 0); } @@ -891,6 +904,8 @@ START_TEST(middlebutton) { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT }, }; + disable_button_scrolling(device); + status = libinput_device_config_middle_emulation_set_enabled( device->libinput_device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); @@ -925,6 +940,8 @@ START_TEST(middlebutton_timeout) enum libinput_config_status status; unsigned int button; + disable_button_scrolling(device); + status = libinput_device_config_middle_emulation_set_enabled( device->libinput_device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); @@ -963,6 +980,8 @@ START_TEST(middlebutton_doubleclick) { BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT }, }; + disable_button_scrolling(device); + status = libinput_device_config_middle_emulation_set_enabled( device->libinput_device, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); @@ -1004,6 +1023,8 @@ START_TEST(middlebutton_middleclick) enum libinput_config_status status; unsigned int button; + disable_button_scrolling(device); + if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) return; @@ -1066,6 +1087,8 @@ START_TEST(middlebutton_middleclick_during) enum libinput_config_status status; unsigned int button; + disable_button_scrolling(device); + if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE)) return; @@ -1238,11 +1261,11 @@ int main (int argc, char **argv) { litest_add("pointer:accel", pointer_accel_defaults_absolute_relative, LITEST_ABSOLUTE|LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:accel", pointer_accel_direction_change, LITEST_RELATIVE, LITEST_ANY); - litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_POINTINGSTICK); - litest_add("pointer:middlebutton", middlebutton_timeout, LITEST_BUTTON, LITEST_POINTINGSTICK); - litest_add("pointer:middlebutton", middlebutton_doubleclick, LITEST_BUTTON, LITEST_POINTINGSTICK); - litest_add("pointer:middlebutton", middlebutton_middleclick, LITEST_BUTTON, LITEST_POINTINGSTICK); - litest_add("pointer:middlebutton", middlebutton_middleclick_during, LITEST_BUTTON, LITEST_POINTINGSTICK); + litest_add("pointer:middlebutton", middlebutton, LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton_timeout, LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton_doubleclick, LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton_middleclick, LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:middlebutton", middlebutton_middleclick_during, LITEST_BUTTON, LITEST_ANY); litest_add("pointer:middlebutton", middlebutton_default_enabled, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_POINTINGSTICK); litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY); litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); From a7b2d04e90cb66526a825deaf38da9924b4fb89e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 16:45:35 +1000 Subject: [PATCH 087/165] evdev: enable button scrolling by default on mice without wheels If we have a middle button but no wheels, enable on-button scrolling for the middle button by default. This applies e.g. to the Logitech trackball added as new test device here. This makes the separate check for POINTINGSTICK obsolete but I'd rather leave this in to be more explicit about it. https://bugs.freedesktop.org/show_bug.cgi?id=90208 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 22 +++++++++-- test/Makefile.am | 1 + test/litest-logitech-trackball.c | 64 ++++++++++++++++++++++++++++++++ test/litest.c | 2 + test/litest.h | 1 + test/pointer.c | 21 +++++++++++ 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 test/litest-logitech-trackball.c diff --git a/src/evdev.c b/src/evdev.c index d65b1131..16ac4997 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -983,8 +983,15 @@ evdev_scroll_get_default_method(struct libinput_device *device) if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; - else - return LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + + /* Mice without a scroll wheel but with middle button have on-button + * scrolling by default */ + if (!libevdev_has_event_code(evdev->evdev, EV_REL, REL_WHEEL) && + !libevdev_has_event_code(evdev->evdev, EV_REL, REL_HWHEEL) && + libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) + return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + + return LIBINPUT_CONFIG_SCROLL_NO_SCROLL; } static enum libinput_config_status @@ -1016,8 +1023,15 @@ evdev_scroll_get_default_button(struct libinput_device *device) if (libevdev_has_property(evdev->evdev, INPUT_PROP_POINTING_STICK)) return BTN_MIDDLE; - else - return 0; + + /* A device that defaults to button scrolling defaults + to BTN_MIDDLE */ + if (evdev_scroll_get_default_method(device) == + LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN && + libevdev_has_event_code(evdev->evdev, EV_KEY, BTN_MIDDLE)) + return BTN_MIDDLE; + + return 0; } static int diff --git a/test/Makefile.am b/test/Makefile.am index a616c5c4..be0c5d90 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -18,6 +18,7 @@ liblitest_la_SOURCES = \ litest-generic-singletouch.c \ litest-keyboard.c \ litest-keyboard-razer-blackwidow.c \ + litest-logitech-trackball.c \ litest-mouse.c \ litest-mouse-roccat.c \ litest-ms-surface-cover.c \ diff --git a/test/litest-logitech-trackball.c b/test/litest-logitech-trackball.c new file mode 100644 index 00000000..5e862d86 --- /dev/null +++ b/test/litest-logitech-trackball.c @@ -0,0 +1,64 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "litest.h" +#include "litest-int.h" + +static void litest_logitech_trackball_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_LOGITECH_TRACKBALL); + litest_set_current_device(d); +} + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x46d, + .product = 0xc408, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_SIDE, + EV_KEY, BTN_EXTRA, + EV_REL, REL_X, + EV_REL, REL_Y, + -1 , -1, +}; + +struct litest_test_device litest_logitech_trackball_device = { + .type = LITEST_LOGITECH_TRACKBALL, + .features = LITEST_RELATIVE | LITEST_BUTTON, + .shortname = "logitech trackball", + .setup = litest_logitech_trackball_setup, + .interface = NULL, + + .name = "Logitech USB Trackball", + .id = &input_id, + .absinfo = NULL, + .events = events, +}; diff --git a/test/litest.c b/test/litest.c index 6d17a375..15fc87ea 100644 --- a/test/litest.c +++ b/test/litest.c @@ -102,6 +102,7 @@ extern struct litest_test_device litest_keyboard_blackwidow_device; extern struct litest_test_device litest_wheel_only_device; extern struct litest_test_device litest_mouse_roccat_device; extern struct litest_test_device litest_ms_surface_cover_device; +extern struct litest_test_device litest_logitech_trackball_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -125,6 +126,7 @@ struct litest_test_device* devices[] = { &litest_wheel_only_device, &litest_mouse_roccat_device, &litest_ms_surface_cover_device, + &litest_logitech_trackball_device, NULL, }; diff --git a/test/litest.h b/test/litest.h index e5a80305..611cdf78 100644 --- a/test/litest.h +++ b/test/litest.h @@ -56,6 +56,7 @@ enum litest_device_type { LITEST_KEYBOARD_BLACKWIDOW = -20, LITEST_WHEEL_ONLY = -21, LITEST_MOUSE_ROCCAT = -22, + LITEST_LOGITECH_TRACKBALL = -23, }; enum litest_device_feature { diff --git a/test/pointer.c b/test/pointer.c index 26d9930c..1dde354d 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -758,6 +758,26 @@ START_TEST(pointer_scroll_button) } END_TEST +START_TEST(pointer_scroll_nowheel_defaults) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_scroll_method method; + uint32_t button; + + method = libinput_device_config_scroll_get_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + + method = libinput_device_config_scroll_get_default_method(device); + ck_assert_int_eq(method, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); + + button = libinput_device_config_scroll_get_button(device); + ck_assert_int_eq(button, BTN_MIDDLE); + button = libinput_device_config_scroll_get_default_button(device); + ck_assert_int_eq(button, BTN_MIDDLE); +} +END_TEST + START_TEST(pointer_accel_defaults) { struct litest_device *dev = litest_current_device(); @@ -1243,6 +1263,7 @@ int main (int argc, char **argv) { litest_add_no_device("pointer:button", pointer_seat_button_count); litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); + litest_add("pointer:scroll", pointer_scroll_nowheel_defaults, LITEST_RELATIVE|LITEST_BUTTON, LITEST_WHEEL); litest_add("pointer:scroll", pointer_scroll_natural_defaults, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_natural_enable_config, LITEST_WHEEL, LITEST_ANY); litest_add("pointer:scroll", pointer_scroll_natural_wheel, LITEST_WHEEL, LITEST_ANY); From 6b6f8151a41147eb44d08f33de143b43eb004563 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 29 Apr 2015 07:53:56 +1000 Subject: [PATCH 088/165] tools: print version number in libinput-list-devices Makes debugging a bit easier when you can just ask users to do that instead of digging around in whatever packaging system they have. Signed-off-by: Peter Hutterer Reviewed-By: Derek Foreman --- tools/libinput-list-devices.c | 38 ++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index 24c7c532..c9e11911 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -30,6 +30,7 @@ #include #include +#include #include "shared.h" @@ -240,6 +241,23 @@ print_device_notify(struct libinput_event *ev) printf("\n"); } +static inline void +usage(void) +{ + printf("Usage: %s [--help|--version]\n" + "\n" + "This tool creates a libinput context on the default seat \"seat0\"\n" + "and lists all devices recognized by libinput and the configuration options.\n" + "Where multiple options are possible, the default is prefixed with \"*\".\n" + "\n" + "Options:\n" + "--help ...... show this help\n" + "--version ... show version information\n" + "\n" + "This tool requires access to the /dev/input/eventX nodes.\n", + program_invocation_short_name); +} + int main(int argc, char **argv) { @@ -248,16 +266,16 @@ main(int argc, char **argv) struct libinput_event *ev; if (argc > 1) { - printf("Usage: %s [--help]\n" - "\n" - "This tool creates a libinput context on the default seat \"seat0\"\n" - "and lists all devices recognized by libinput and the configuration options.\n" - "Where multiple options are possible, the default is prefixed with \"*\".\n" - "\n" - "This tool requires access to the /dev/input/eventX nodes.\n", - program_invocation_short_name); - - return 1; + if (strcmp(argv[1], "--help") == 0) { + usage(); + return 0; + } else if (strcmp(argv[1], "--version") == 0) { + printf("%s\n", LIBINPUT_VERSION); + return 0; + } else { + usage(); + return 1; + } } tools_init_options(&options); From 412bb6196d78038b34fd22d8c2d3319e8cdff619 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 12:36:28 +1000 Subject: [PATCH 089/165] tools: fix typo in man page Signed-off-by: Peter Hutterer --- tools/libinput-list-devices.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/libinput-list-devices.man b/tools/libinput-list-devices.man index afa90905..cd89283a 100644 --- a/tools/libinput-list-devices.man +++ b/tools/libinput-list-devices.man @@ -1,4 +1,4 @@ -.TH LIBINPUT-LIST_DEVICES "1" +.TH LIBINPUT-LIST-DEVICES "1" .SH NAME libinput-list-devices \- list local devices as recognized by libinput .SH SYNOPSIS From a21ab9eb98cfd580670f5cf6ef4e5f3a01fc5df9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 15:34:01 +1000 Subject: [PATCH 090/165] evdev: indentation fix Signed-off-by: Peter Hutterer --- src/evdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index 16ac4997..d997d24c 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -281,7 +281,10 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time) } /* Apply pointer acceleration. */ - accel = filter_dispatch(device->pointer.filter, &unaccel, device, time); + accel = filter_dispatch(device->pointer.filter, + &unaccel, + device, + time); if (normalized_is_zero(accel) && normalized_is_zero(unaccel)) break; From b6238b82863c690215472b5f4eb2049b12745a0a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 14:02:30 +1000 Subject: [PATCH 091/165] tools: fix broken build if builddir != srcdir Introduced in 6b6f8151a41147eb44d08f33de143b43eb004563, libinput-version.h is in the builddir. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index ae3a27f1..792f3107 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -3,8 +3,8 @@ bin_PROGRAMS = libinput-list-devices noinst_LTLIBRARIES = libshared.la AM_CPPFLAGS = -I$(top_srcdir)/include \ - -I$(top_srcdir)/src - + -I$(top_srcdir)/src \ + -I$(top_builddir)/src # for libinput-version.h libshared_la_SOURCES = \ shared.c \ From 2d86dc2c6e63dea3ae37b5ede349a387da0c90d6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 11:24:48 +1000 Subject: [PATCH 092/165] timer: drain data on the timerfd when it triggers Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/timer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/timer.c b/src/timer.c index f6c8e427..285f75b2 100644 --- a/src/timer.c +++ b/src/timer.c @@ -101,6 +101,9 @@ libinput_timer_handler(void *data) struct libinput *libinput = data; struct libinput_timer *timer, *tmp; uint64_t now; + uint64_t discard; + + read(libinput->timer.fd, &discard, sizeof(discard)); now = libinput_now(libinput); if (now == 0) From db03e59c5221f5cd1670695f21066b1d3d8d4c9e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 29 Apr 2015 14:53:27 +1000 Subject: [PATCH 093/165] test: add a note to the touch double-down event libevdev filters the ABS_MT_TRACKING_ID event for a double-touch down like this so we never see this in libinput. We see an error message from libevdev but otherwise this test is a false negative. Signed-off-by: Peter Hutterer --- test/touch.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/touch.c b/test/touch.c index eb9f4cb6..a5eb553d 100644 --- a/test/touch.c +++ b/test/touch.c @@ -182,6 +182,9 @@ START_TEST(touch_double_touch_down_up) dev = litest_current_device(); libinput = dev->libinput; + /* note: this test is a false negative, libevdev will filter + * tracking IDs re-used in the same slot. */ + litest_touch_down(dev, 0, 0, 0); litest_touch_down(dev, 0, 0, 0); litest_touch_up(dev, 0); From 8e667070ff3cd468121ecc0960d51210ae4b9ef8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 12:41:50 +1000 Subject: [PATCH 094/165] tools: ship the event-debug tool as an installed libinput-debug-events tool Rebuild the same binary but without the special LDFLAG. The event-debug tool is left as-is to allow for easy debugging with gdb, the new tool is now libtool-enabled and can't be run directly in gdb without installing it first. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/.gitignore | 1 + tools/Makefile.am | 7 ++++++- tools/libinput-debug-events.man | 31 +++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tools/libinput-debug-events.man diff --git a/tools/.gitignore b/tools/.gitignore index e58dba95..cb934298 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -2,3 +2,4 @@ event-debug event-gui ptraccel-debug libinput-list-devices +libinput-debug-events diff --git a/tools/Makefile.am b/tools/Makefile.am index 792f3107..6fb797f2 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,5 +1,5 @@ noinst_PROGRAMS = event-debug ptraccel-debug -bin_PROGRAMS = libinput-list-devices +bin_PROGRAMS = libinput-list-devices libinput-debug-events noinst_LTLIBRARIES = libshared.la AM_CPPFLAGS = -I$(top_srcdir)/include \ @@ -26,6 +26,11 @@ libinput_list_devices_LDADD = ../src/libinput.la libshared.la $(LIBUDEV_LIBS) libinput_list_devices_CFLAGS = $(LIBUDEV_CFLAGS) dist_man1_MANS = libinput-list-devices.man +libinput_debug_events_SOURCES = $(event_debug_SOURCES) +libinput_debug_events_LDADD = $(event_debug_LDADD) +libinput_debug_events_CFLAGS = $(event_debug_CFLAGS) +dist_man1_MANS = libinput-debug-events.man + if BUILD_EVENTGUI noinst_PROGRAMS += event-gui diff --git a/tools/libinput-debug-events.man b/tools/libinput-debug-events.man new file mode 100644 index 00000000..bb393992 --- /dev/null +++ b/tools/libinput-debug-events.man @@ -0,0 +1,31 @@ +.TH LIBINPUT-DEBUG-EVENTS "1" +.SH NAME +libinput-debug-events \- debug helper for libinput +.SH SYNOPSIS +.B libinput-debug-events [--help] +.SH DESCRIPTION +.PP +The +.I libinput-debug-events +tool creates a libinput context and prints all events from these devices. +.PP +This is a debugging tool only, its output may change at any time. Do not +rely on the output. +.PP +This tool usually needs to be run as root to have access to the +/dev/input/eventX nodes. +.SH OPTIONS +.TP 8 +.B --help +Print help +.PP +For all other options, see the output from --help. Options may be added or +removed at any time. +.SH NOTES +.PP +Events shown by this tool may not correspond to the events seen by a +different user of libinput. This tool initializes a separate context. +.PP +Events shown by this tool include key codes in plain text. Anything you type +while this tool is running will show up in the output, including your +passwords. From 28ed7a62653f1e257bba22b792ec785e9200d40e Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 30 Apr 2015 11:43:28 -0500 Subject: [PATCH 095/165] Set O_CLOEXEC when opening devices We'd rather keep these out of the hands of children. Signed-off-by: Derek Foreman Reviewed-by: David Herrmann Signed-off-by: Peter Hutterer --- src/evdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index d997d24c..af36127a 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1953,7 +1953,8 @@ evdev_device_create(struct libinput_seat *seat, /* Use non-blocking mode so that we can loop on read on * evdev_device_data() until all events on the fd are * read. mtdev_get() also expects this. */ - fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK); + fd = open_restricted(libinput, devnode, + O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { log_info(libinput, "opening input device '%s' failed (%s).\n", @@ -2436,7 +2437,8 @@ evdev_device_resume(struct evdev_device *device) return -ENODEV; devnode = udev_device_get_devnode(device->udev_device); - fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK); + fd = open_restricted(libinput, devnode, + O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) return -errno; From c2ef1854d82ebd6f484c59759f69dd30b4b3593b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 1 May 2015 09:09:38 +1000 Subject: [PATCH 096/165] timer: set O_NONBLOCK on the timerfd Resetting a timerfd empties the data on the fd, so if the timer is reset between triggering and us reading it, we may block trying to read it. Since we read events off a device in a loop, a device sending a continuous flow of events may cause the timer to trigger but delay reading it. If one of the events cause e.g. the tap timer to be set, the timerfd may be empty at the time of reading. Suggested-by: Derek Foreman Signed-off-by: Peter Hutterer --- src/timer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/timer.c b/src/timer.c index 285f75b2..114a6496 100644 --- a/src/timer.c +++ b/src/timer.c @@ -122,7 +122,8 @@ libinput_timer_handler(void *data) int libinput_timer_subsys_init(struct libinput *libinput) { - libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC, + TFD_CLOEXEC | TFD_NONBLOCK); if (libinput->timer.fd < 0) return -1; From 8ead828e6f1e1ce4b38a2a2d90518ba012e32df9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 1 May 2015 11:41:12 +1000 Subject: [PATCH 097/165] Fix a couple of coding style issues Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-tap.c | 8 ++++---- test/build-pedantic.c | 3 ++- test/log.c | 3 ++- test/misc.c | 3 ++- test/pointer.c | 4 ++-- test/touchpad.c | 4 ++-- test/trackpoint.c | 4 ++-- tools/ptraccel-debug.c | 3 ++- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 0f25e267..7d1fc848 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -59,8 +59,8 @@ enum tap_event { */ static inline const char* -tap_state_to_str(enum tp_tap_state state) { - +tap_state_to_str(enum tp_tap_state state) +{ switch(state) { CASE_RETURN_STRING(TAP_STATE_IDLE); CASE_RETURN_STRING(TAP_STATE_HOLD); @@ -82,8 +82,8 @@ tap_state_to_str(enum tp_tap_state state) { } static inline const char* -tap_event_to_str(enum tap_event event) { - +tap_event_to_str(enum tap_event event) +{ switch(event) { CASE_RETURN_STRING(TAP_EVENT_TOUCH); CASE_RETURN_STRING(TAP_EVENT_MOTION); diff --git a/test/build-pedantic.c b/test/build-pedantic.c index 920fc4ab..f602127d 100644 --- a/test/build-pedantic.c +++ b/test/build-pedantic.c @@ -3,6 +3,7 @@ /* This is a build-test only */ int -main(void) { +main(void) +{ return 0; } diff --git a/test/log.c b/test/log.c index a56af151..139c0020 100644 --- a/test/log.c +++ b/test/log.c @@ -140,7 +140,8 @@ START_TEST(log_priority) } END_TEST -int main (int argc, char **argv) { +int main (int argc, char **argv) +{ litest_add_no_device("log:defaults", log_default_priority); litest_add_no_device("log:logging", log_handler_invoked); litest_add_no_device("log:logging", log_handler_NULL); diff --git a/test/misc.c b/test/misc.c index 829da7d3..e9d54622 100644 --- a/test/misc.c +++ b/test/misc.c @@ -583,7 +583,8 @@ START_TEST(trackpoint_accel_parser) } END_TEST -int main (int argc, char **argv) { +int main (int argc, char **argv) +{ litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); diff --git a/test/pointer.c b/test/pointer.c index 1dde354d..a0f862b8 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -1253,8 +1253,8 @@ START_TEST(middlebutton_default_touchpad) } END_TEST -int main (int argc, char **argv) { - +int main (int argc, char **argv) +{ litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY); diff --git a/test/touchpad.c b/test/touchpad.c index 830e5890..094f671e 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4116,8 +4116,8 @@ START_TEST(touchpad_trackpoint_no_trackpoint) } END_TEST -int main(int argc, char **argv) { - +int main(int argc, char **argv) +{ litest_add("touchpad:motion", touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); diff --git a/test/trackpoint.c b/test/trackpoint.c index 2708bad2..ac6173e4 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -130,8 +130,8 @@ START_TEST(trackpoint_scroll_source) } END_TEST -int main(int argc, char **argv) { - +int main(int argc, char **argv) +{ litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY); litest_add("trackpoint:middlebutton", trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY); litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY); diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index d00e8f37..fdd8490e 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -178,7 +178,8 @@ usage(void) } int -main(int argc, char **argv) { +main(int argc, char **argv) +{ struct motion_filter *filter; double step = 0.1, max_dx = 10; From 6dd02468ac00cb5ce0ce0a90d7b5482ba683c88c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 10:21:02 +1000 Subject: [PATCH 098/165] test: reduce the multitap tests to 5 taps only Reduces the risk of accidental timeouts Signed-off-by: Peter Hutterer --- test/touchpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index 094f671e..114ae2c7 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -390,7 +390,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) litest_drain_events(li); - for (i = 3; i < 8; i++) { + for (i = 3; i < 5; i++) { for (ntaps = 0; ntaps <= i; ntaps++) { litest_touch_down(dev, 0, 50, 50); @@ -453,7 +453,7 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) litest_drain_events(li); - for (i = 3; i < 8; i++) { + for (i = 3; i < 5; i++) { for (ntaps = 0; ntaps <= i; ntaps++) { litest_touch_down(dev, 0, 50, 50); From d8ec73aead8206b84efdac20d1aa4bd41b89254e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 08:42:44 +1000 Subject: [PATCH 099/165] test: add timeout_tapndrag() Prep work for the upcoming patch to extend the timeout for tap-and-drag. And switch the tests that rely on it over to the new function. Signed-off-by: Peter Hutterer --- test/litest.c | 6 ++++++ test/litest.h | 1 + test/touchpad.c | 1 + 3 files changed, 8 insertions(+) diff --git a/test/litest.c b/test/litest.c index 15fc87ea..91f27473 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1430,6 +1430,12 @@ litest_timeout_tap(void) msleep(200); } +void +litest_timeout_tapndrag(void) +{ + msleep(520); +} + void litest_timeout_softbuttons(void) { diff --git a/test/litest.h b/test/litest.h index 611cdf78..f1a720c8 100644 --- a/test/litest.h +++ b/test/litest.h @@ -196,6 +196,7 @@ struct libevdev_uinput * litest_create_uinput_abs_device(const char *name, ...); void litest_timeout_tap(void); +void litest_timeout_tapndrag(void); void litest_timeout_softbuttons(void); void litest_timeout_buttonscroll(void); void litest_timeout_edgescroll(void); diff --git a/test/touchpad.c b/test/touchpad.c index 114ae2c7..576b2bd8 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -574,6 +574,7 @@ START_TEST(touchpad_1fg_tap_n_drag_timeout) litest_assert_empty_queue(li); litest_touch_up(dev, 0); + litest_timeout_tapndrag(); litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED); From 182b7b7da92e634d6bd3786076014d1f9e4d5121 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Apr 2015 09:50:02 +1000 Subject: [PATCH 100/165] touchpad: fix double/multitap timeouts The current doubletap timeout was incorrect, it gave the user only 180ms to touch, release, touch, release to recognise a doubletap. But it would also set a timeout on the second release, delaying the button events by 180ms. Instead, re-arm the timer on the second touch down. This gives the user 180ms to release and touch again (or time out). This makes doubletap much more reliable and reduces the delay between the release and the button events arriving since the finger down time is already counted. Same fix for MULTITAP, though we already armed the timer on touch down so we just have to remove the new timer on touch release which did little but delay everything. https://bugs.freedesktop.org/show_bug.cgi?id=90172 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 7d1fc848..50e15129 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -224,6 +224,7 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP; + tp_tap_set_timer(tp, time); break; case TAP_EVENT_TIMEOUT: tp->tap.state = TAP_STATE_IDLE; @@ -355,7 +356,6 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_MULTITAP; tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_set_timer(tp, time); break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: @@ -487,7 +487,6 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_MULTITAP; tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_set_timer(tp, time); break; case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DRAGGING_2; From ff132b2cfbb6edb407f80c4bd238dc75eb97c7f1 Mon Sep 17 00:00:00 2001 From: Velimir Lisec Date: Thu, 30 Apr 2015 11:12:26 +0200 Subject: [PATCH 101/165] touchpad: increase drag timeout libinput supports lifting a finger during dragging and setting it back down again to continue the drag. Curently the drag timeout is set to DEFAULT_TAP_TIMEOUT. That is to short, when we're dragging the finger needs to have enough time to move from one edge of the touchpad to the other. 180ms is too short for that and causes false timeouts and thus button releases that cancel the drag. Introduce DEFAULT_DRAG_TIMEOUT and set it to 500 ms. Signed-off-by: Velimir Lisec Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 50e15129..70c45ff3 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -37,6 +37,7 @@ #define CASE_RETURN_STRING(a) case a: return #a #define DEFAULT_TAP_TIMEOUT_PERIOD 180 +#define DEFAULT_DRAG_TIMEOUT_PERIOD 500 #define DEFAULT_TAP_MOVE_THRESHOLD TP_MM_TO_DPI_NORMALIZED(3) enum tap_event { @@ -128,6 +129,12 @@ tp_tap_set_timer(struct tp_dispatch *tp, uint64_t time) libinput_timer_set(&tp->tap.timer, time + DEFAULT_TAP_TIMEOUT_PERIOD); } +static void +tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time) +{ + libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAG_TIMEOUT_PERIOD); +} + static void tp_tap_clear_timer(struct tp_dispatch *tp) { @@ -380,7 +387,7 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_DRAGGING_WAIT; - tp_tap_set_timer(tp, time); + tp_tap_set_drag_timer(tp, time); break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: From 14025099db42ad96e4a2e973862c48d5a6753a60 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 14:14:54 +1000 Subject: [PATCH 102/165] tools: add to, don't replace, the man page list Signed-off-by: Peter Hutterer --- tools/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index 6fb797f2..b24c560d 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -29,7 +29,7 @@ dist_man1_MANS = libinput-list-devices.man libinput_debug_events_SOURCES = $(event_debug_SOURCES) libinput_debug_events_LDADD = $(event_debug_LDADD) libinput_debug_events_CFLAGS = $(event_debug_CFLAGS) -dist_man1_MANS = libinput-debug-events.man +dist_man1_MANS += libinput-debug-events.man if BUILD_EVENTGUI noinst_PROGRAMS += event-gui From f52dbc5ad9f482e93f65038407c06183c60a5062 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 23 Apr 2015 12:06:42 +1000 Subject: [PATCH 103/165] touchpad: enable clickfinger by default on chromebooks This matches the vendor default. Board IDs pulled from modinfo chromeos_laptop, touchpad names from a bit of googling around. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 5 ++++- src/evdev.c | 1 + src/evdev.h | 1 + udev/90-libinput-model-quirks.hwdb | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 7f60a53a..608befea 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -646,9 +646,12 @@ tp_button_config_click_get_method(struct libinput_device *device) static enum libinput_config_click_method tp_click_get_default_method(struct tp_dispatch *tp) { + struct evdev_device *device = tp->device; + if (!tp->buttons.is_clickpad) return LIBINPUT_CONFIG_CLICK_METHOD_NONE; - else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE) + else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE || + device->model == EVDEV_MODEL_CHROMEBOOK) return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; else return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; diff --git a/src/evdev.c b/src/evdev.c index af36127a..ee8dfe6e 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1447,6 +1447,7 @@ evdev_read_model(struct evdev_device *device) enum evdev_device_model model; } model_map[] = { { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 }, + { "LIBINPUT_MODEL_CHROMEBOOK", EVDEV_MODEL_CHROMEBOOK }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index 151d1031..2529c198 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -96,6 +96,7 @@ enum evdev_middlebutton_event { enum evdev_device_model { EVDEV_MODEL_DEFAULT, EVDEV_MODEL_LENOVO_X230, + EVDEV_MODEL_CHROMEBOOK, }; struct mt_slot { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 959be0ff..d6a7c91f 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -15,6 +15,28 @@ # # Sort by brand, model +########################################## +# Google +########################################## + +# The various chromebooks, info from modinfo chromeos_laptop, touchpad names +# extrapolated from the chromiumos touchad-tests repo +# https://chromium.googlesource.com/chromiumos/platform/touchpad-tests +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pnFalco:pvr* +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Mario*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Butterfly*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Peppy*: +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*ZGB*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*pn*Parrot*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Leon*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Falco*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*bvn*coreboot*:pn*Wolf*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*GOOGLE*:pn*Link*: +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pn*Alex*: +libinput:name:Cypress APA Trackpad (cyapa):dmi:*svn*SAMSUNG*:pn*Lumpy*: +libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*: + LIBINPUT_MODEL_CHROMEBOOK=1 + ########################################## # LENOVO ########################################## From d6867dfae013d7924dbf4c6aa71e7f7c04be75b2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 29 Apr 2015 11:03:08 +1000 Subject: [PATCH 104/165] touchpad: enable clickfinger by default on some System76 laptops Bonobo, Clevo, Galago and Kudu have clickpads and no markings -> enable clickfinger by default. Lemur and Gazelle have physical buttons, no need for extra configuration. https://bugs.freedesktop.org/show_bug.cgi?id=90170 Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-buttons.c | 18 ++++++++++++++---- src/evdev.c | 4 ++++ src/evdev.h | 4 ++++ udev/90-libinput-model-quirks.hwdb | 20 ++++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index 608befea..d6acbf0a 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -650,11 +650,21 @@ tp_click_get_default_method(struct tp_dispatch *tp) if (!tp->buttons.is_clickpad) return LIBINPUT_CONFIG_CLICK_METHOD_NONE; - else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE || - device->model == EVDEV_MODEL_CHROMEBOOK) + else if (libevdev_get_id_vendor(tp->device->evdev) == VENDOR_ID_APPLE) return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; - else - return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + + switch (device->model) { + case EVDEV_MODEL_CHROMEBOOK: + case EVDEV_MODEL_SYSTEM76_BONOBO: + case EVDEV_MODEL_SYSTEM76_CLEVO: + case EVDEV_MODEL_SYSTEM76_GALAGO: + case EVDEV_MODEL_SYSTEM76_KUDU: + return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + default: + break; + } + + return LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; } static enum libinput_config_click_method diff --git a/src/evdev.c b/src/evdev.c index ee8dfe6e..4ce92501 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1448,6 +1448,10 @@ evdev_read_model(struct evdev_device *device) } model_map[] = { { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 }, { "LIBINPUT_MODEL_CHROMEBOOK", EVDEV_MODEL_CHROMEBOOK }, + { "LIBINPUT_MODEL_SYSTEM76_BONOBO", EVDEV_MODEL_SYSTEM76_BONOBO }, + { "LIBINPUT_MODEL_SYSTEM76_CLEVO", EVDEV_MODEL_SYSTEM76_CLEVO }, + { "LIBINPUT_MODEL_SYSTEM76_GALAGO", EVDEV_MODEL_SYSTEM76_GALAGO }, + { "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index 2529c198..20c0b556 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -97,6 +97,10 @@ enum evdev_device_model { EVDEV_MODEL_DEFAULT, EVDEV_MODEL_LENOVO_X230, EVDEV_MODEL_CHROMEBOOK, + EVDEV_MODEL_SYSTEM76_BONOBO, + EVDEV_MODEL_SYSTEM76_CLEVO, + EVDEV_MODEL_SYSTEM76_GALAGO, + EVDEV_MODEL_SYSTEM76_KUDU, }; struct mt_slot { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index d6a7c91f..0cdf2b2b 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -44,3 +44,23 @@ libinput:name:Atmel maXTouch Touchpad:dmi:*svn*GOOGLE*:pn*Samus*: # X230 (Tablet) libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*:pvrThinkPadX230* LIBINPUT_MODEL_LENOVO_X230=1 + +########################################## +# System76 +########################################## + +# Bonobo Professional +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrbonp5* + LIBINPUT_MODEL_SYSTEM76_BONOBO=1 + +# Clevo +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pnW740SU*rnW740SU* + LIBINPUT_MODEL_SYSTEM76_CLEVO=1 + +# Galago Ultra Pro +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrgalu1* + LIBINPUT_MODEL_SYSTEM76_GALAGO=1 + +# Kudu Professional +libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrkudp1* + LIBINPUT_MODEL_SYSTEM76_KUDU=1 From c9a3c7a7e324459f2f57d17f2c334750c9f78494 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 16:22:36 +1000 Subject: [PATCH 105/165] touchpad: drop the tap finger count Use tp->nfingers_down as trigger when we have no fingers left on the touchpad and when we should return to idle. If all touchpoints end in the same frame tp->nfingers is 0. Thus when we handle the first tap release we transition to IDLE which now needs to handle (and discard) any touch release events. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-tap.c | 6 ++---- src/evdev-mt-touchpad.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 70c45ff3..61f94e51 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -154,6 +154,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: + break; case TAP_EVENT_MOTION: log_bug_libinput(libinput, "invalid tap event, no fingers are down\n"); @@ -531,7 +532,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_RELEASE: - if (tp->tap.tap_finger_count == 0) + if (tp->nfingers_down == 0) tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_TOUCH: @@ -652,7 +653,6 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.state = TAP_TOUCH_STATE_DEAD; if (t->state == TOUCH_BEGIN) { - tp->tap.tap_finger_count++; t->tap.state = TAP_TOUCH_STATE_TOUCH; t->tap.initial = t->point; tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); @@ -665,7 +665,6 @@ 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.tap_finger_count--; 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 && @@ -850,7 +849,6 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) } tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE; - tp->tap.tap_finger_count = 0; } void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 97b17cde..d4f5874c 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -257,7 +257,6 @@ struct tp_dispatch { struct libinput_timer timer; enum tp_tap_state state; uint32_t buttons_pressed; - unsigned int tap_finger_count; uint64_t multitap_last_time; } tap; From bb2678bfe88eb3e64f211533d776d6c277e731a8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Apr 2015 14:48:42 +1000 Subject: [PATCH 106/165] touchpad: when clearing the touchpad state, release fake touches too Causes an error message in the device_disable_release_tap_n_drag test. When the touchpad is suspended, all touches are ended in tp_clear_state. Since the hovering support was added, this returns the touches to TOUCH_HOVERING, a subsequent tp_handle_state() will turn them back into TOUCH_BEGIN based on BTN_TOUCH and BTN_TOOL_FINGER still being down. Clear the fake touch buttons as well after ending the touches, this way the touch points are reset to TOUCH_NONE as intended. Once we do that we don't need to manually change the tap finger count when releasing taps, we can just let the count reset naturally. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index d5ce8803..e017559d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -749,6 +749,12 @@ tp_destroy(struct evdev_dispatch *dispatch) free(tp); } +static void +tp_release_fake_touches(struct tp_dispatch *tp) +{ + tp->fake_touches = 0; +} + static void tp_clear_state(struct tp_dispatch *tp) { @@ -772,6 +778,7 @@ tp_clear_state(struct tp_dispatch *tp) tp_for_each_touch(tp, t) { tp_end_sequence(tp, t, now); } + tp_release_fake_touches(tp); tp_handle_state(tp, now); } From 91ae98c09c2ea61e3ace816c19a953a4111a2647 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 16:44:34 +1000 Subject: [PATCH 107/165] test: add helper function for creating a device not expected to be added No functional changes Signed-off-by: Peter Hutterer --- test/device.c | 89 +++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/test/device.c b/test/device.c index b6031896..450360c1 100644 --- a/test/device.c +++ b/test/device.c @@ -814,11 +814,26 @@ START_TEST(abs_mt_device_no_absx) } END_TEST -START_TEST(abs_device_no_range) +static void +assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo) { struct libevdev_uinput *uinput; - struct libinput *li; struct libinput_device *device; + + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + device = libinput_path_add_device(li, + libevdev_uinput_get_devnode(uinput)); + ck_assert(device == NULL); + libevdev_uinput_destroy(uinput); +} + +START_TEST(abs_device_no_range) +{ + struct libinput *li; int code; /* set x/y so libinput doesn't just reject for missing axes */ struct input_absinfo absinfo[] = { @@ -833,15 +848,7 @@ START_TEST(abs_device_no_range) for (code = 0; code < ABS_MISC; code++) { absinfo[2].value = code; - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + assert_device_ignored(li, absinfo); } litest_restore_log_handler(li); @@ -851,9 +858,7 @@ END_TEST START_TEST(abs_mt_device_no_range) { - struct libevdev_uinput *uinput; struct libinput *li; - struct libinput_device *device; int code; /* set x/y so libinput doesn't just reject for missing axes */ struct input_absinfo absinfo[] = { @@ -876,15 +881,7 @@ START_TEST(abs_mt_device_no_range) continue; absinfo[6].value = code; - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + assert_device_ignored(li, absinfo); } litest_restore_log_handler(li); @@ -894,9 +891,7 @@ END_TEST START_TEST(abs_device_missing_res) { - struct libevdev_uinput *uinput; struct libinput *li; - struct libinput_device *device; struct input_absinfo absinfo[] = { { ABS_X, 0, 10, 0, 0, 10 }, { ABS_Y, 0, 10, 0, 0, 0 }, @@ -905,39 +900,22 @@ START_TEST(abs_device_missing_res) li = litest_create_context(); litest_disable_log_handler(li); - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + + assert_device_ignored(li, absinfo); absinfo[0].resolution = 0; absinfo[1].resolution = 20; - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + + assert_device_ignored(li, absinfo); litest_restore_log_handler(li); libinput_unref(li); - } END_TEST START_TEST(abs_mt_device_missing_res) { - struct libevdev_uinput *uinput; struct libinput *li; - struct libinput_device *device; struct input_absinfo absinfo[] = { { ABS_X, 0, 10, 0, 0, 10 }, { ABS_Y, 0, 10, 0, 0, 10 }, @@ -950,27 +928,12 @@ START_TEST(abs_mt_device_missing_res) li = litest_create_context(); litest_disable_log_handler(li); - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + assert_device_ignored(li, absinfo); absinfo[4].resolution = 0; absinfo[5].resolution = 20; - uinput = litest_create_uinput_abs_device("test device", NULL, - absinfo, - EV_KEY, BTN_LEFT, - EV_KEY, BTN_RIGHT, - -1); - device = libinput_path_add_device(li, - libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); - libevdev_uinput_destroy(uinput); + + assert_device_ignored(li, absinfo); litest_restore_log_handler(li); libinput_unref(li); From 2bf8d035c417995b6700c55c2af32d766a47b4d4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 16:46:18 +1000 Subject: [PATCH 108/165] test: split test for invalid ranges into hi/lo This test can randomly trigger a timeout, depending on how busy the machine is. Signed-off-by: Peter Hutterer --- test/device.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/test/device.c b/test/device.c index 450360c1..1c277ba4 100644 --- a/test/device.c +++ b/test/device.c @@ -831,7 +831,7 @@ assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo) libevdev_uinput_destroy(uinput); } -START_TEST(abs_device_no_range) +START_TEST(abs_device_no_range_lo) { struct libinput *li; int code; @@ -846,7 +846,32 @@ START_TEST(abs_device_no_range) li = litest_create_context(); litest_disable_log_handler(li); - for (code = 0; code < ABS_MISC; code++) { + for (code = 0; code < ABS_MISC/2; code++) { + absinfo[2].value = code; + assert_device_ignored(li, absinfo); + } + + litest_restore_log_handler(li); + libinput_unref(li); +} +END_TEST + +START_TEST(abs_device_no_range_hi) +{ + struct libinput *li; + int code; + /* set x/y so libinput doesn't just reject for missing axes */ + struct input_absinfo absinfo[] = { + { ABS_X, 0, 10, 0, 0, 0 }, + { ABS_Y, 0, 10, 0, 0, 0 }, + { -1, 0, 0, 0, 0, 0 }, + { -1, -1, -1, -1, -1, -1 } + }; + + li = litest_create_context(); + litest_disable_log_handler(li); + + for (code = ABS_MISC/2; code < ABS_MISC; code++) { absinfo[2].value = code; assert_device_ignored(li, absinfo); } @@ -983,7 +1008,8 @@ int main (int argc, char **argv) litest_add_no_device("device:invalid devices", abs_device_no_absy); litest_add_no_device("device:invalid devices", abs_mt_device_no_absx); litest_add_no_device("device:invalid devices", abs_mt_device_no_absy); - litest_add_no_device("device:invalid devices", abs_device_no_range); + litest_add_no_device("device:invalid devices", abs_device_no_range_hi); + litest_add_no_device("device:invalid devices", abs_device_no_range_lo); litest_add_no_device("device:invalid devices", abs_mt_device_no_range); litest_add_no_device("device:invalid devices", abs_device_missing_res); litest_add_no_device("device:invalid devices", abs_mt_device_missing_res); From 103fad15e7fb78d7d8ae489d9e427dc61a8f693d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 16:01:59 +1000 Subject: [PATCH 109/165] configure.ac: libinput 0.15.0 Signed-off-by: Peter Hutterer --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8ecbf92e..405edc3c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) -m4_define([libinput_minor_version], [14]) +m4_define([libinput_minor_version], [15]) m4_define([libinput_micro_version], [1]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) @@ -31,7 +31,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) # b) If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # c) If the interface is the same as the previous version, change to C:R+1:A -LIBINPUT_LT_VERSION=11:1:1 +LIBINPUT_LT_VERSION=12:0:2 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) From 12df380698531472d495534ed356722478563707 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 17:20:35 +1000 Subject: [PATCH 110/165] configure.ac: libinput 0.15.0 - this time for real Signed-off-by: Peter Hutterer --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 405edc3c..cdde4fae 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.64]) m4_define([libinput_major_version], [0]) m4_define([libinput_minor_version], [15]) -m4_define([libinput_micro_version], [1]) +m4_define([libinput_micro_version], [0]) m4_define([libinput_version], [libinput_major_version.libinput_minor_version.libinput_micro_version]) From 38448563027435caea09372e7aac4cac4d92b1c0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 10:49:17 +1000 Subject: [PATCH 111/165] test: move a comment to where it belongs Signed-off-by: Peter Hutterer --- test/litest.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/litest.c b/test/litest.c index 91f27473..d3b5d6cb 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1246,11 +1246,6 @@ litest_create_uinput_device_from_description(const char *name, libevdev_free(dev); - /* uinput does not yet support setting the resolution, so we set it - * afterwards. This is of course racy as hell but the way we - * _generally_ use this function by the time libinput uses the - * device, we're finished here */ - devnode = libevdev_uinput_get_devnode(uinput); ck_assert_notnull(devnode); fd = open(devnode, O_RDONLY); @@ -1258,6 +1253,10 @@ litest_create_uinput_device_from_description(const char *name, rc = libevdev_new_from_fd(fd, &dev); ck_assert_int_eq(rc, 0); + /* uinput does not yet support setting the resolution, so we set it + * afterwards. This is of course racy as hell but the way we + * _generally_ use this function by the time libinput uses the + * device, we're finished here */ abs = abs_info; while (abs && abs->value != -1) { if (abs->resolution != 0) { From 1ea69203acd49d5b1182a184babbc05f096be939 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 11:55:48 +1000 Subject: [PATCH 112/165] test: return the right value from open_restricted fd or negative errno on failure. Signed-off-by: Peter Hutterer --- test/litest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/litest.c b/test/litest.c index d3b5d6cb..d0043da2 100644 --- a/test/litest.c +++ b/test/litest.c @@ -393,7 +393,8 @@ litest_log_handler(struct libinput *libinput, static int open_restricted(const char *path, int flags, void *userdata) { - return open(path, flags); + int fd = open(path, flags); + return fd < 0 ? -errno : fd; } static void From e7572274df07b80de3fc79012bb1992442edd54d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 12:16:15 +1000 Subject: [PATCH 113/165] doc: add a page about the test suite Signed-off-by: Peter Hutterer --- doc/Makefile.am | 3 ++- doc/test-suite.dox | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 doc/test-suite.dox diff --git a/doc/Makefile.am b/doc/Makefile.am index 6e6f72d3..3d81d7ce 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,7 +18,8 @@ header_files = \ $(srcdir)/scrolling.dox \ $(srcdir)/seats.dox \ $(srcdir)/t440-support.dox \ - $(srcdir)/tapping.dox + $(srcdir)/tapping.dox \ + $(srcdir)/test-suite.dox diagram_files = \ $(srcdir)/dot/seats-sketch.gv \ diff --git a/doc/test-suite.dox b/doc/test-suite.dox new file mode 100644 index 00000000..6e993e08 --- /dev/null +++ b/doc/test-suite.dox @@ -0,0 +1,66 @@ +/** +@page test-suite libinput test suite + +The libinput test suite is based on +[Check](http://check.sourceforge.net/doc/check_html/) and runs automatically +during `make check`. Check itself is wrapped into a libinput-specific test +suite called *litest*. Tests are found in `$srcdir/test/`, the test binaries are +prefixed with `test-` and can be run individually. + +@section test-config X.Org config to avoid interference + +uinput devices created by the test suite are usually recognised by X as +input devices. All events sent through these devices will generate X events +and interfere with your desktop. + +Copy the file `$srcdir/test/50-litest.conf` into your `/etc/X11/xorg.conf.d` +and restart X. This will ignore any litest devices and thus not interfere +with your desktop. + +@section test-root Permissions required to run tests + +Most tests require the creation of uinput devices and access to the +resulting `/dev/input/eventX` nodes. Some tests require temporary udev rules. +This usually requires the tests to be run as root. + +@section test-filtering Selective running of tests + +Check enables tests to be grouped into suites and test cases, litest uses +test suites as a general feature-specific grouping (e.g. "touchpad:tap") and +instantiates one test case per device. The --list flag shows the list of +suites and tests. +@code +$ ./test/test-device --list +device:wheel: + wheel only + blackwidow +device:invalid devices: + no device +device:group: + no device + logitech trackball + MS surface cover + mouse_roccat + wheel only + blackwidow +... +@endcode + +In the above example, the "device:wheel" suite is run for the "wheel only" and +the "blackwidow" device. Both devices are automatically instantiated through +uinput by litest. The "no device" entry signals that litest does not +instantiate a uinput device for a specific test. + +Check provides two filters through environment variables: CK_RUN_SUITE +and CK_RUN_CASE. They may be used independently or combined to narrow +down the set of tests to run. For example: + +@code +$ CK_RUN_SUITE="device:wheel" ./test/test-device +$ CK_RUN_CASE="wheel only" ./test/test-device +$ CK_RUN_SUITE="device:wheel" CK_RUN_CASE="wheel only" ./test/test-device +@endcode + +Check and litest currently do not provide a way to run a specific test +function only. +*/ From 22a06792b1cc4e13901ff4eb5c14e2ed16707894 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 30 Apr 2015 16:30:23 -0400 Subject: [PATCH 114/165] test: bcm5974: down should be marked as static Signed-off-by: Benjamin Tissoires Signed-off-by: Peter Hutterer --- test/litest-bcm5974.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest-bcm5974.c b/test/litest-bcm5974.c index 035bed22..ddff0387 100644 --- a/test/litest-bcm5974.c +++ b/test/litest-bcm5974.c @@ -33,7 +33,7 @@ static void litest_bcm5974_setup(void) litest_set_current_device(d); } -struct input_event down[] = { +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 }, From 92fc816bfe1b1a15dd4835d6544005db58411c0c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 30 Apr 2015 16:30:24 -0400 Subject: [PATCH 115/165] test: rename hover tests into semi_mt_hover The current hover tests are uniquely designed for some Synaptics touchpad. libinput can handle hovering through ABS_MT_DISTANCE, so we need to reserve the "hover" name for real hovering devices. Signed-off-by: Benjamin Tissoires Signed-off-by: Peter Hutterer --- test/touchpad.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index 576b2bd8..450409e8 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3481,7 +3481,7 @@ hover_start(struct litest_device *dev, unsigned int slot, /* WARNING: no SYN_REPORT! */ } -START_TEST(touchpad_hover_noevent) +START_TEST(touchpad_semi_mt_hover_noevent) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -3512,7 +3512,7 @@ START_TEST(touchpad_hover_noevent) } END_TEST -START_TEST(touchpad_hover_down) +START_TEST(touchpad_semi_mt_hover_down) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -3587,7 +3587,7 @@ START_TEST(touchpad_hover_down) } END_TEST -START_TEST(touchpad_hover_down_hover_down) +START_TEST(touchpad_semi_mt_hover_down_hover_down) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -3674,7 +3674,7 @@ START_TEST(touchpad_hover_down_hover_down) } END_TEST -START_TEST(touchpad_hover_down_up) +START_TEST(touchpad_semi_mt_hover_down_up) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -3741,7 +3741,7 @@ START_TEST(touchpad_hover_down_up) } END_TEST -START_TEST(touchpad_hover_2fg_noevent) +START_TEST(touchpad_semi_mt_hover_2fg_noevent) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -3786,7 +3786,7 @@ START_TEST(touchpad_hover_2fg_noevent) } END_TEST -START_TEST(touchpad_hover_2fg_1fg_down) +START_TEST(touchpad_semi_mt_hover_2fg_1fg_down) { struct litest_device *dev = litest_current_device(); struct libinput *li = dev->libinput; @@ -4233,14 +4233,14 @@ int main(int argc, char **argv) litest_add("touchpad:left-handed", touchpad_left_handed_delayed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:left-handed", touchpad_left_handed_clickpad_delayed, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); - /* Hover tests aren't generic, they only work on this device and + /* Semi-MT hover tests aren't generic, they only work on this device and * ignore the semi-mt capability (it doesn't matter for the tests */ - litest_add_for_device("touchpad:hover", touchpad_hover_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); - litest_add_for_device("touchpad:hover", touchpad_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); - litest_add_for_device("touchpad:hover", touchpad_hover_down_up, LITEST_SYNAPTICS_HOVER_SEMI_MT); - litest_add_for_device("touchpad:hover", touchpad_hover_down_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); - litest_add_for_device("touchpad:hover", touchpad_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); - litest_add_for_device("touchpad:hover", touchpad_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down_up, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_down_hover_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); 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); From 2af94a5dfd422c555ca0d1b6a27dfee8d9152867 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 13:14:23 +1000 Subject: [PATCH 116/165] filter: add Simon's copyright MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code was largely lifted from the X server in bb25b2ad297891430606c367bfabc but didn't take the copyright messages that applied to that code. Signed-off-by: Peter Hutterer Acked-by: Jonas Ådahl Acked-by: Simon Thum --- COPYING | 1 + src/filter.c | 1 + 2 files changed, 2 insertions(+) diff --git a/COPYING b/COPYING index 8bbb3c38..efc1a949 100644 --- a/COPYING +++ b/COPYING @@ -1,3 +1,4 @@ +Copyright © 2006-2009 Simon Thum Copyright © 2008-2012 Kristian Høgsberg Copyright © 2010-2012 Intel Corporation Copyright © 2010-2011 Benjamin Franzke diff --git a/src/filter.c b/src/filter.c index b953bee8..626cb0aa 100644 --- a/src/filter.c +++ b/src/filter.c @@ -1,4 +1,5 @@ /* + * Copyright © 2006-2009 Simon Thum * Copyright © 2012 Jonas Ådahl * * Permission to use, copy, modify, distribute, and sell this software and From 0cd36225d73e609647208c0b3201769ac921a3ee Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 6 May 2015 19:41:25 -0400 Subject: [PATCH 117/165] touchpad: add support for per-finger hovering information When the device supports true hovering, it reports this information through ABS_MT_DISTANCE. When this axis is available, we should rely on it to (un)hover the touches as BTN_TOUCH is most of the time unreliable (generated by the mouse emulation in the kernel). Signed-off-by: Benjamin Tissoires Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 41 ++++++++- src/evdev-mt-touchpad.h | 2 + test/Makefile.am | 1 + test/litest-atmel-hover.c | 149 +++++++++++++++++++++++++++++++ test/litest.c | 156 ++++++++++++++++++++++++++++++--- test/litest.h | 24 ++++- test/touchpad.c | 180 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 537 insertions(+), 16 deletions(-) create mode 100644 test/litest-atmel-hover.c diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e017559d..79177fb0 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -300,6 +300,9 @@ 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); @@ -520,7 +523,29 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) } static void -tp_unhover_touches(struct tp_dispatch *tp, uint64_t time) +tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time) +{ + struct tp_touch *t; + unsigned int i; + + for (i = 0; i < tp->ntouches; i++) { + t = tp_get_touch(tp, i); + + 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); + } + } else { + if (t->distance > 0) + tp_end_touch(tp, t, time); + } + } +} + +static void +tp_unhover_fake_touches(struct tp_dispatch *tp, uint64_t time) { struct tp_touch *t; unsigned int nfake_touches; @@ -578,6 +603,16 @@ tp_unhover_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); + else + tp_unhover_fake_touches(tp, time); + +} + static void tp_process_state(struct tp_dispatch *tp, uint64_t time) { @@ -1191,6 +1226,10 @@ tp_init(struct tp_dispatch *tp, device->abs.absinfo_y->minimum); diagonal = sqrt(width*width + height*height); + tp->reports_distance = libevdev_has_event_code(device->evdev, + EV_ABS, + ABS_MT_DISTANCE); + tp->hysteresis_margin.x = diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR; tp->hysteresis_margin.y = diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index d4f5874c..37113752 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -129,6 +129,7 @@ struct tp_touch { bool dirty; struct device_coords point; uint64_t millis; + int distance; /* distance == 0 means touch */ struct { struct device_coords samples[TOUCHPAD_HISTORY_LENGTH]; @@ -183,6 +184,7 @@ struct tp_dispatch { unsigned int slot; /* current slot */ bool has_mt; bool semi_mt; + bool reports_distance; /* does the device support true hovering */ enum touchpad_model model; unsigned int num_slots; /* number of slots */ diff --git a/test/Makefile.am b/test/Makefile.am index be0c5d90..a90fa78a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -14,6 +14,7 @@ liblitest_la_SOURCES = \ litest.h \ litest-int.h \ litest-alps-semi-mt.c \ + litest-atmel-hover.c \ litest-bcm5974.c \ litest-generic-singletouch.c \ litest-keyboard.c \ diff --git a/test/litest-atmel-hover.c b/test/litest-atmel-hover.c new file mode 100644 index 00000000..7da0901b --- /dev/null +++ b/test/litest-atmel-hover.c @@ -0,0 +1,149 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "libinput-util.h" + +#include "litest.h" +#include "litest-int.h" + +static void +atmel_hover_create(struct litest_device *d); + +static void +litest_atmel_hover_setup(void) +{ + struct litest_device *d = litest_create_device(LITEST_ATMEL_HOVER); + litest_set_current_device(d); +} + +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_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_ABS, .code = ABS_MT_PRESSURE, .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_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + { .type = -1, .code = -1 }, +}; + +static 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_DISTANCE, .value = 1 }, + { .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + { .type = -1, .code = -1 }, +}; + +static struct litest_device_interface interface = { + .touch_down_events = down, + .touch_move_events = move, + .touch_up_events = up, +}; + +static struct input_id input_id = { + .bustype = 0x18, + .vendor = 0x0, + .product = 0x0, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_TOOL_FINGER, + EV_KEY, BTN_TOUCH, + EV_KEY, BTN_TOOL_DOUBLETAP, + EV_KEY, BTN_TOOL_TRIPLETAP, + EV_KEY, BTN_TOOL_QUADTAP, + EV_KEY, BTN_TOOL_QUINTTAP, + INPUT_PROP_MAX, INPUT_PROP_POINTER, + INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD, + -1, -1, +}; + +static struct input_absinfo absinfo[] = { + { ABS_X, 0, 960, 0, 0, 10 }, + { ABS_Y, 0, 540, 0, 0, 10 }, + { ABS_PRESSURE, 0, 255, 0, 0, 0 }, + { ABS_MT_SLOT, 0, 9, 0, 0, 0 }, + { ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0, 0 }, + { ABS_MT_ORIENTATION, 0, 255, 0, 0, 0 }, + { ABS_MT_POSITION_X, 0, 960, 0, 0, 10 }, + { ABS_MT_POSITION_Y, 0, 540, 0, 0, 10 }, + { ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 }, + { ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 }, + { ABS_MT_PRESSURE, 0, 255, 0, 0, 0 }, + { ABS_MT_DISTANCE, 0, 1, 0, 0, 0 }, + { .value = -1 } +}; + +struct litest_test_device litest_atmel_hover_device = { + .type = LITEST_ATMEL_HOVER, + .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD | LITEST_HOVER, + .shortname = "atmel hover", + .setup = litest_atmel_hover_setup, + .interface = &interface, + .create = atmel_hover_create, + + .name = "Atmel maXTouch Touchpad", + .id = &input_id, + .events = events, + .absinfo = absinfo, +}; + +static void +atmel_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_atmel_hover_device.name, + litest_atmel_hover_device.id, + absinfo, + events); + d->interface = &interface; +} diff --git a/test/litest.c b/test/litest.c index d0043da2..21ddca20 100644 --- a/test/litest.c +++ b/test/litest.c @@ -103,6 +103,7 @@ extern struct litest_test_device litest_wheel_only_device; extern struct litest_test_device litest_mouse_roccat_device; extern struct litest_test_device litest_ms_surface_cover_device; extern struct litest_test_device litest_logitech_trackball_device; +extern struct litest_test_device litest_atmel_hover_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -127,6 +128,7 @@ struct litest_test_device* devices[] = { &litest_mouse_roccat_device, &litest_ms_surface_cover_device, &litest_logitech_trackball_device, + &litest_atmel_hover_device, NULL, }; @@ -795,7 +797,8 @@ litest_event(struct litest_device *d, unsigned int type, int litest_auto_assign_value(struct litest_device *d, const struct input_event *ev, - int slot, double x, double y) + int slot, double x, double y, + bool touching) { static int tracking_id; int value = ev->value; @@ -818,6 +821,9 @@ litest_auto_assign_value(struct litest_device *d, case ABS_MT_SLOT: value = slot; break; + case ABS_MT_DISTANCE: + value = touching ? 0 : 1; + break; } return value; @@ -834,9 +840,9 @@ send_btntool(struct litest_device *d) litest_event(d, EV_KEY, BTN_TOOL_QUINTTAP, d->ntouches_down == 5); } -void -litest_touch_down(struct litest_device *d, unsigned int slot, - double x, double y) +static void +litest_slot_start(struct litest_device *d, unsigned int slot, + double x, double y, bool touching) { struct input_event *ev; @@ -852,12 +858,25 @@ litest_touch_down(struct litest_device *d, unsigned int slot, ev = d->interface->touch_down_events; while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) { - int value = litest_auto_assign_value(d, ev, slot, x, y); + int value = litest_auto_assign_value(d, + ev, + slot, + x, + y, + touching); + litest_event(d, ev->type, ev->code, value); ev++; } } +void +litest_touch_down(struct litest_device *d, unsigned int slot, + double x, double y) +{ + litest_slot_start(d, slot, x, y, 1); +} + void litest_touch_up(struct litest_device *d, unsigned int slot) { @@ -883,15 +902,20 @@ litest_touch_up(struct litest_device *d, unsigned int slot) 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); + int value = litest_auto_assign_value(d, + ev, + slot, + 0, + 0, + false); litest_event(d, ev->type, ev->code, value); ev++; } } -void -litest_touch_move(struct litest_device *d, unsigned int slot, - double x, double y) +static void +litest_slot_move(struct litest_device *d, unsigned int slot, + double x, double y, bool touching) { struct input_event *ev; @@ -902,12 +926,24 @@ litest_touch_move(struct litest_device *d, unsigned int slot, 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); + int value = litest_auto_assign_value(d, + ev, + slot, + x, + y, + touching); litest_event(d, ev->type, ev->code, value); ev++; } } +void +litest_touch_move(struct litest_device *d, unsigned int slot, + double x, double y) +{ + litest_slot_move(d, slot, x, y, true); +} + void litest_touch_move_to(struct litest_device *d, unsigned int slot, @@ -950,6 +986,98 @@ litest_touch_move_two_touches(struct litest_device *d, litest_touch_move(d, 1, x1 + dx, y1 + dy); } +void +litest_hover_start(struct litest_device *d, unsigned int slot, + double x, double y) +{ + litest_slot_start(d, slot, x, y, 0); +} + +void +litest_hover_end(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_DISTANCE, .value = 1 }, + { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + { .type = -1, .code = -1 } + }; + + assert(d->ntouches_down > 0); + d->ntouches_down--; + + send_btntool(d); + + 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, 0); + litest_event(d, ev->type, ev->code, value); + ev++; + } +} + +void +litest_hover_move(struct litest_device *d, unsigned int slot, + double x, double y) +{ + litest_slot_move(d, slot, x, y, false); +} + +void +litest_hover_move_to(struct litest_device *d, + unsigned int slot, + double x_from, double y_from, + double x_to, double y_to, + int steps, int sleep_ms) +{ + for (int i = 0; i < steps - 1; i++) { + litest_hover_move(d, slot, + x_from + (x_to - x_from)/steps * i, + y_from + (y_to - y_from)/steps * i); + if (sleep_ms) { + libinput_dispatch(d->libinput); + msleep(sleep_ms); + libinput_dispatch(d->libinput); + } + } + litest_hover_move(d, slot, x_to, y_to); +} + +void +litest_hover_move_two_touches(struct litest_device *d, + double x0, double y0, + double x1, double y1, + double dx, double dy, + int steps, int sleep_ms) +{ + for (int i = 0; i < steps - 1; i++) { + litest_push_event_frame(d); + litest_hover_move(d, 0, x0 + dx / steps * i, + y0 + dy / steps * i); + litest_hover_move(d, 1, x1 + dx / steps * i, + y1 + dy / steps * i); + litest_pop_event_frame(d); + if (sleep_ms) { + libinput_dispatch(d->libinput); + msleep(sleep_ms); + libinput_dispatch(d->libinput); + } + } + litest_push_event_frame(d); + litest_hover_move(d, 0, x0 + dx, y0 + dy); + litest_hover_move(d, 1, x1 + dx, y1 + dy); + litest_pop_event_frame(d); +} + void litest_button_click(struct litest_device *d, unsigned int button, bool is_press) { @@ -1490,11 +1618,11 @@ send_abs_xy(struct litest_device *d, double x, double y) e.type = EV_ABS; e.code = ABS_X; e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); + val = litest_auto_assign_value(d, &e, 0, x, y, true); litest_event(d, EV_ABS, ABS_X, val); e.code = ABS_Y; - val = litest_auto_assign_value(d, &e, 0, x, y); + val = litest_auto_assign_value(d, &e, 0, x, y, true); litest_event(d, EV_ABS, ABS_Y, val); } @@ -1507,12 +1635,12 @@ send_abs_mt_xy(struct litest_device *d, double x, double y) e.type = EV_ABS; e.code = ABS_MT_POSITION_X; e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); + val = litest_auto_assign_value(d, &e, 0, x, y, true); litest_event(d, EV_ABS, ABS_MT_POSITION_X, val); e.code = ABS_MT_POSITION_Y; e.value = LITEST_AUTO_ASSIGN; - val = litest_auto_assign_value(d, &e, 0, x, y); + val = litest_auto_assign_value(d, &e, 0, x, y, true); litest_event(d, EV_ABS, ABS_MT_POSITION_Y, val); } diff --git a/test/litest.h b/test/litest.h index f1a720c8..23a8e879 100644 --- a/test/litest.h +++ b/test/litest.h @@ -57,6 +57,7 @@ enum litest_device_type { LITEST_WHEEL_ONLY = -21, LITEST_MOUSE_ROCCAT = -22, LITEST_LOGITECH_TRACKBALL = -23, + LITEST_ATMEL_HOVER = -24, }; enum litest_device_feature { @@ -77,6 +78,7 @@ enum litest_device_feature { LITEST_FAKE_MT = 1 << 12, LITEST_ABSOLUTE = 1 << 13, LITEST_PROTOCOL_A = 1 << 14, + LITEST_HOVER = 1 << 15, }; struct litest_device { @@ -141,7 +143,8 @@ void litest_event(struct litest_device *t, int value); int litest_auto_assign_value(struct litest_device *d, const struct input_event *ev, - int slot, double x, double y); + int slot, double x, double y, + bool touching); void litest_touch_up(struct litest_device *d, unsigned int slot); void litest_touch_move(struct litest_device *d, unsigned int slot, @@ -161,6 +164,25 @@ void litest_touch_move_two_touches(struct litest_device *d, double x1, double y1, double dx, double dy, int steps, int sleep_ms); +void litest_hover_start(struct litest_device *d, + unsigned int slot, + double x, + double y); +void litest_hover_end(struct litest_device *d, unsigned int slot); +void litest_hover_move(struct litest_device *d, + unsigned int slot, + double x, + double y); +void litest_hover_move_to(struct litest_device *d, + unsigned int slot, + double x_from, double y_from, + double x_to, double y_to, + int steps, int sleep_ms); +void litest_hover_move_two_touches(struct litest_device *d, + double x0, double y0, + double x1, double y1, + double dx, double dy, + int steps, int sleep_ms); void litest_button_click(struct litest_device *d, unsigned int button, bool is_press); diff --git a/test/touchpad.c b/test/touchpad.c index 450409e8..c482d8df 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3835,6 +3835,179 @@ START_TEST(touchpad_semi_mt_hover_2fg_1fg_down) } END_TEST +START_TEST(touchpad_hover_noevent) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + litest_hover_start(dev, 0, 50, 50); + litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + litest_hover_end(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + /* hover the finger */ + litest_hover_start(dev, 0, 50, 50); + + litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + + litest_assert_empty_queue(li); + + /* touch the finger on the sensor */ + litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10); + + libinput_dispatch(li); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + /* go back to hover */ + litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + litest_hover_end(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_down_hover_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + + litest_drain_events(li); + + litest_hover_start(dev, 0, 50, 50); + + for (i = 0; i < 3; i++) { + + /* hover the finger */ + litest_hover_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + + litest_assert_empty_queue(li); + + /* touch the finger */ + litest_touch_move_to(dev, 0, 70, 70, 50, 50, 10, 10); + + libinput_dispatch(li); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + } + + litest_hover_end(dev, 0); + + /* start a new touch to be sure */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + litest_touch_up(dev, 0); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); +} +END_TEST + +START_TEST(touchpad_hover_down_up) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + /* hover two fingers, and a touch */ + litest_push_event_frame(dev); + litest_hover_start(dev, 0, 50, 50); + litest_hover_start(dev, 1, 50, 50); + litest_touch_down(dev, 2, 50, 50); + litest_pop_event_frame(dev);; + + litest_assert_empty_queue(li); + + /* hover first finger, end second and third in same frame */ + litest_push_event_frame(dev); + litest_hover_move(dev, 0, 70, 70); + litest_hover_end(dev, 1); + litest_touch_up(dev, 2); + litest_pop_event_frame(dev);; + + litest_assert_empty_queue(li); + + /* now move the finger */ + litest_touch_move_to(dev, 0, 50, 50, 70, 70, 10, 10); + + litest_touch_up(dev, 0); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); +} +END_TEST + +START_TEST(touchpad_hover_2fg_noevent) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + litest_drain_events(li); + + /* hover two fingers */ + litest_push_event_frame(dev); + litest_hover_start(dev, 0, 25, 25); + litest_hover_start(dev, 1, 50, 50); + litest_pop_event_frame(dev);; + + litest_hover_move_two_touches(dev, 25, 25, 50, 50, 50, 50, 10, 0); + + litest_push_event_frame(dev); + litest_hover_end(dev, 0); + litest_hover_end(dev, 1); + litest_pop_event_frame(dev);; + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_hover_2fg_1fg_down) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + int i; + + litest_drain_events(li); + + /* hover two fingers */ + litest_push_event_frame(dev); + litest_hover_start(dev, 0, 25, 25); + litest_touch_down(dev, 1, 50, 50); + litest_pop_event_frame(dev);; + + for (i = 0; i < 10; i++) { + litest_push_event_frame(dev); + litest_hover_move(dev, 0, 25 + 5 * i, 25 + 5 * i); + litest_touch_move(dev, 1, 50 + 5 * i, 50 - 5 * i); + litest_pop_event_frame(dev);; + } + + litest_push_event_frame(dev); + litest_hover_end(dev, 0); + litest_touch_up(dev, 1); + litest_pop_event_frame(dev);; + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); +} +END_TEST + static void assert_btnevent_from_device(struct litest_device *device, unsigned int button, @@ -4242,6 +4415,13 @@ int main(int argc, char **argv) litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_noevent, LITEST_SYNAPTICS_HOVER_SEMI_MT); litest_add_for_device("touchpad:semi-mt-hover", touchpad_semi_mt_hover_2fg_1fg_down, LITEST_SYNAPTICS_HOVER_SEMI_MT); + litest_add("touchpad:hover", touchpad_hover_noevent, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY); + litest_add("touchpad:hover", touchpad_hover_down, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY); + litest_add("touchpad:hover", touchpad_hover_down_up, LITEST_TOUCHPAD|LITEST_HOVER, LITEST_ANY); + 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_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); litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_mb_click, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); From 799ee6c53ea8679b6c57bf073a928cd2996b1cb4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 09:30:51 +1000 Subject: [PATCH 118/165] test: litest_is_button_event should take an unsigned int Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 2 +- test/litest.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/litest.c b/test/litest.c index 21ddca20..e39af5f8 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1457,7 +1457,7 @@ litest_create_uinput_device(const char *name, struct input_id *id, ...) struct libinput_event_pointer* litest_is_button_event(struct libinput_event *event, - int button, + unsigned int button, enum libinput_button_state state) { struct libinput_event_pointer *ptrev; diff --git a/test/litest.h b/test/litest.h index 23a8e879..83859c2d 100644 --- a/test/litest.h +++ b/test/litest.h @@ -198,7 +198,7 @@ void litest_drain_events(struct libinput *li); void litest_assert_empty_queue(struct libinput *li); struct libinput_event_pointer * litest_is_button_event( struct libinput_event *event, - int button, + unsigned int button, enum libinput_button_state state); void litest_assert_button_event(struct libinput *li, unsigned int button, From 6c2d2d7e6c298f0e78646a8f96065d874ee6cfe1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 20:55:37 +1000 Subject: [PATCH 119/165] test: re-order the tests to have the likely ones to fail first Run the touchpad tests first, it's the most likely to fail. It also takes the longest, so it's annoying to have it fail when you spent a minute watching the other tests succeed. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/Makefile.am | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index a90fa78a..0df89b2c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -40,16 +40,16 @@ liblitest_la_SOURCES = \ liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la run_tests = \ - test-udev \ - test-path \ + test-touchpad \ + test-device \ test-pointer \ test-touch \ - test-log \ - test-touchpad \ test-trackpoint \ + test-udev \ + test-path \ + test-log \ test-misc \ - test-keyboard \ - test-device + test-keyboard build_tests = \ test-build-cxx \ test-build-linker \ From 2e38547c7965611773af0674efc93860dd11c479 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 13:11:21 +1000 Subject: [PATCH 120/165] test: add litest_is_axis_event() helper Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 25 +++++++++++++++++++++---- test/litest.h | 4 ++++ test/pointer.c | 14 ++++---------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/test/litest.c b/test/litest.c index e39af5f8..a374608d 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1474,6 +1474,26 @@ litest_is_button_event(struct libinput_event *event, return ptrev; } +struct libinput_event_pointer * +litest_is_axis_event(struct libinput_event *event, + enum libinput_pointer_axis axis, + enum libinput_pointer_axis_source source) +{ + struct libinput_event_pointer *ptrev; + enum libinput_event_type type = LIBINPUT_EVENT_POINTER_AXIS; + + ck_assert(event != NULL); + ck_assert_int_eq(libinput_event_get_type(event), type); + ptrev = libinput_event_get_pointer_event(event); + ck_assert(libinput_event_pointer_has_axis(ptrev, axis)); + + if (source != 0) + ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev), + source); + + return ptrev; +} + void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state) @@ -1501,10 +1521,7 @@ litest_assert_scroll(struct libinput *li, ck_assert(next_event != NULL); /* At least 1 scroll + stop scroll */ while (event) { - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_AXIS); - ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); + ptrev = litest_is_axis_event(event, axis, 0); if (next_event) { /* Normal scroll event, check dir */ diff --git a/test/litest.h b/test/litest.h index 83859c2d..fa910ed0 100644 --- a/test/litest.h +++ b/test/litest.h @@ -200,6 +200,10 @@ struct libinput_event_pointer * litest_is_button_event( struct libinput_event *event, unsigned int button, enum libinput_button_state state); +struct libinput_event_pointer * litest_is_axis_event( + struct libinput_event *event, + enum libinput_pointer_axis axis, + enum libinput_pointer_axis_source source); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); diff --git a/test/pointer.c b/test/pointer.c index a0f862b8..7784941f 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -383,22 +383,16 @@ test_wheel_event(struct litest_device *dev, int which, int amount) libinput_dispatch(li); - event = libinput_get_event(li); - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_AXIS); - - ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); - axis = (which == REL_WHEEL) ? LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL : LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL; + event = libinput_get_event(li); + ptrev = litest_is_axis_event(event, + axis, + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL); ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis), expected); - ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev), - LIBINPUT_POINTER_AXIS_SOURCE_WHEEL); ck_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis), discrete); libinput_event_destroy(event); From 77eb2d5445c2a04c1e8d51c840c4986fff3fe389 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 13:17:27 +1000 Subject: [PATCH 121/165] test: add litest_is_motion_event() helper Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 23 +++++++++++++++++++++++ test/litest.h | 2 ++ test/pointer.c | 14 ++------------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/test/litest.c b/test/litest.c index a374608d..ca996e4a 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1494,6 +1494,29 @@ litest_is_axis_event(struct libinput_event *event, return ptrev; } +struct libinput_event_pointer * +litest_is_motion_event(struct libinput_event *event) +{ + struct libinput_event_pointer *ptrev; + enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION; + double x, y, ux, uy; + + ck_assert(event != NULL); + ck_assert_int_eq(libinput_event_get_type(event), type); + ptrev = libinput_event_get_pointer_event(event); + + x = libinput_event_pointer_get_dx(ptrev); + y = libinput_event_pointer_get_dy(ptrev); + ux = libinput_event_pointer_get_dx_unaccelerated(ptrev); + uy = libinput_event_pointer_get_dy_unaccelerated(ptrev); + + /* No 0 delta motion events */ + ck_assert(x != 0.0 || y != 0.0 || + ux != 0.0 || uy != 0.0); + + return ptrev; +} + void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state) diff --git a/test/litest.h b/test/litest.h index fa910ed0..fa974958 100644 --- a/test/litest.h +++ b/test/litest.h @@ -204,6 +204,8 @@ struct libinput_event_pointer * litest_is_axis_event( struct libinput_event *event, enum libinput_pointer_axis axis, enum libinput_pointer_axis_source source); +struct libinput_event_pointer * litest_is_motion_event( + struct libinput_event *event); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); diff --git a/test/pointer.c b/test/pointer.c index 7784941f..43c21ba3 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -42,12 +42,7 @@ get_accelerated_motion_event(struct libinput *li) while (1) { event = libinput_get_event(li); - ck_assert_notnull(event); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_MOTION); - - ptrev = libinput_event_get_pointer_event(event); - ck_assert_notnull(ptrev); + ptrev = litest_is_motion_event(event); if (fabs(libinput_event_pointer_get_dx(ptrev)) < DBL_MIN && fabs(libinput_event_pointer_get_dy(ptrev)) < DBL_MIN) { @@ -190,12 +185,7 @@ test_unaccel_event(struct litest_device *dev, int dx, int dy) libinput_dispatch(li); event = libinput_get_event(li); - ck_assert_notnull(event); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_MOTION); - - ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); + ptrev = litest_is_motion_event(event); ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev); ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev); From cbfbede7484b6652f29663817e6da0377aa3ffb8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 13:51:39 +1000 Subject: [PATCH 122/165] test: add litest_is_touch_event() helper function Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 27 +++++++++++++++++++++++++++ test/litest.h | 3 +++ test/touch.c | 14 ++++---------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/test/litest.c b/test/litest.c index ca996e4a..6506af08 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1531,6 +1531,33 @@ litest_assert_button_event(struct libinput *li, unsigned int button, libinput_event_destroy(event); } +struct libinput_event_touch * +litest_is_touch_event(struct libinput_event *event, + enum libinput_event_type type) +{ + struct libinput_event_touch *touch; + + ck_assert(event != NULL); + + if (type == 0) + type = libinput_event_get_type(event); + + switch (type) { + case LIBINPUT_EVENT_TOUCH_DOWN: + case LIBINPUT_EVENT_TOUCH_UP: + case LIBINPUT_EVENT_TOUCH_MOTION: + case LIBINPUT_EVENT_TOUCH_FRAME: + ck_assert_int_eq(libinput_event_get_type(event), type); + break; + default: + ck_abort_msg("%s: invalid touch type %d\n", __func__, type); + } + + touch = libinput_event_get_touch_event(event); + + return touch; +} + void litest_assert_scroll(struct libinput *li, enum libinput_pointer_axis axis, diff --git a/test/litest.h b/test/litest.h index fa974958..ebe2bea3 100644 --- a/test/litest.h +++ b/test/litest.h @@ -206,6 +206,9 @@ struct libinput_event_pointer * litest_is_axis_event( enum libinput_pointer_axis_source source); struct libinput_event_pointer * litest_is_motion_event( struct libinput_event *event); +struct libinput_event_touch * litest_is_touch_event( + struct libinput_event *event, + enum libinput_event_type type); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); diff --git a/test/touch.c b/test/touch.c index a5eb553d..d78deedd 100644 --- a/test/touch.c +++ b/test/touch.c @@ -244,9 +244,7 @@ START_TEST(touch_calibration_scale) litest_wait_for_event(li); ev = libinput_get_event(li); - ck_assert_int_eq(libinput_event_get_type(ev), - LIBINPUT_EVENT_TOUCH_DOWN); - tev = libinput_event_get_touch_event(ev); + tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN); x = libinput_event_touch_get_x_transformed(tev, width); y = libinput_event_touch_get_y_transformed(tev, height); @@ -315,9 +313,7 @@ START_TEST(touch_calibration_rotation) litest_touch_up(dev, 0); litest_wait_for_event(li); ev = libinput_get_event(li); - ck_assert_int_eq(libinput_event_get_type(ev), - LIBINPUT_EVENT_TOUCH_DOWN); - tev = libinput_event_get_touch_event(ev); + tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN); x = libinput_event_touch_get_x_transformed(tev, width); y = libinput_event_touch_get_y_transformed(tev, height); @@ -381,9 +377,7 @@ START_TEST(touch_calibration_translation) litest_wait_for_event(li); ev = libinput_get_event(li); - ck_assert_int_eq(libinput_event_get_type(ev), - LIBINPUT_EVENT_TOUCH_DOWN); - tev = libinput_event_get_touch_event(ev); + tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN); x = libinput_event_touch_get_x_transformed(tev, width); y = libinput_event_touch_get_y_transformed(tev, height); @@ -499,7 +493,7 @@ START_TEST(touch_protocol_a_touch) litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TOUCH_DOWN, -1); ev = libinput_get_event(li); - tev = libinput_event_get_touch_event(ev); + tev = litest_is_touch_event(ev, LIBINPUT_EVENT_TOUCH_DOWN); oldx = libinput_event_touch_get_x(tev); oldy = libinput_event_touch_get_y(tev); From fd301dfba23d882f7d7cd28a7baaadda2e9760da Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 15:43:08 +1000 Subject: [PATCH 123/165] test: add litest_is_keyboard_event() helper function Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/keyboard.c | 45 ++++++++++++++------------------------------- test/litest.c | 20 ++++++++++++++++++++ test/litest.h | 4 ++++ 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/test/keyboard.c b/test/keyboard.c index 508362bb..03d62ddd 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -61,11 +61,9 @@ START_TEST(keyboard_seat_key_count) continue; } - kev = libinput_event_get_keyboard_event(ev); - ck_assert_notnull(kev); - ck_assert_int_eq(libinput_event_keyboard_get_key(kev), KEY_A); - ck_assert_int_eq(libinput_event_keyboard_get_key_state(kev), - LIBINPUT_KEY_STATE_PRESSED); + kev = litest_is_keyboard_event(ev, + KEY_A, + LIBINPUT_KEY_STATE_PRESSED); ++expected_key_button_count; seat_key_count = @@ -175,31 +173,6 @@ START_TEST(keyboard_ignore_no_pressed_release) } END_TEST -static void -test_key_event(struct litest_device *dev, unsigned int key, int state) -{ - struct libinput *li = dev->libinput; - struct libinput_event *event; - struct libinput_event_keyboard *kevent; - - litest_event(dev, EV_KEY, key, state); - litest_event(dev, EV_SYN, SYN_REPORT, 0); - - libinput_dispatch(li); - - event = libinput_get_event(li); - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_KEYBOARD_KEY); - - kevent = libinput_event_get_keyboard_event(event); - ck_assert(kevent != NULL); - ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key); - ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), - state ? LIBINPUT_KEY_STATE_PRESSED : - LIBINPUT_KEY_STATE_RELEASED); - libinput_event_destroy(event); -} - START_TEST(keyboard_key_auto_release) { struct libinput *libinput; @@ -244,7 +217,17 @@ START_TEST(keyboard_key_auto_release) /* Send pressed events, without releasing */ for (i = 0; i < ARRAY_LENGTH(keys); ++i) { - test_key_event(dev, keys[i].code, 1); + key = keys[i].code; + litest_event(dev, EV_KEY, key, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + libinput_dispatch(libinput); + + event = libinput_get_event(libinput); + kevent = litest_is_keyboard_event(event, + key, + LIBINPUT_KEY_STATE_PRESSED); + libinput_event_destroy(event); } litest_drain_events(libinput); diff --git a/test/litest.c b/test/litest.c index 6506af08..c24a3061 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1558,6 +1558,26 @@ litest_is_touch_event(struct libinput_event *event, return touch; } +struct libinput_event_keyboard * +litest_is_keyboard_event(struct libinput_event *event, + unsigned int key, + enum libinput_key_state state) +{ + struct libinput_event_keyboard *kevent; + enum libinput_event_type type = LIBINPUT_EVENT_KEYBOARD_KEY; + + ck_assert_notnull(event); + ck_assert_int_eq(libinput_event_get_type(event), type); + + kevent = libinput_event_get_keyboard_event(event); + ck_assert_notnull(kevent); + + ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key); + ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), + state); + return kevent; +} + void litest_assert_scroll(struct libinput *li, enum libinput_pointer_axis axis, diff --git a/test/litest.h b/test/litest.h index ebe2bea3..664b49ac 100644 --- a/test/litest.h +++ b/test/litest.h @@ -209,6 +209,10 @@ struct libinput_event_pointer * litest_is_motion_event( struct libinput_event_touch * litest_is_touch_event( struct libinput_event *event, enum libinput_event_type type); +struct libinput_event_keyboard * litest_is_keyboard_event( + struct libinput_event *event, + unsigned int key, + enum libinput_key_state state); void litest_assert_button_event(struct libinput *li, unsigned int button, enum libinput_button_state state); From 4a56ce17f0f8b7a227104575331c6e163273b5c6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 13:57:31 +1000 Subject: [PATCH 124/165] test: use litest_is_button_event() helper Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index c482d8df..ad9e130c 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4015,20 +4015,12 @@ assert_btnevent_from_device(struct litest_device *device, { struct libinput *li = device->libinput; struct libinput_event *e; - struct libinput_event_pointer *pev; libinput_dispatch(li); e = libinput_get_event(li); - ck_assert_notnull(e); - ck_assert_int_eq(libinput_event_get_type(e), - LIBINPUT_EVENT_POINTER_BUTTON); - pev = libinput_event_get_pointer_event(e); + litest_is_button_event(e, button, state); ck_assert_ptr_eq(libinput_event_get_device(e), device->libinput_device); - ck_assert_int_eq(libinput_event_pointer_get_button(pev), - button); - ck_assert_int_eq(libinput_event_pointer_get_button_state(pev), - state); libinput_event_destroy(e); } From 12000e737e5f57180826c8178aa291b8706db6bc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 23 Apr 2015 17:33:17 +1000 Subject: [PATCH 125/165] tools: add middle button emulation to listed capabilities Signed-off-by: Peter Hutterer --- tools/libinput-list-devices.c | 37 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index c9e11911..825969d5 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -100,6 +100,18 @@ nat_scroll_default(struct libinput_device *device) return "disabled"; } +static const char * +middle_emulation_default(struct libinput_device *device) +{ + if (!libinput_device_config_middle_emulation_is_available(device)) + return "n/a"; + + if (libinput_device_config_middle_emulation_get_default_enabled(device)) + return "enabled"; + else + return "disabled"; +} + static char * calibration_default(struct libinput_device *device) { @@ -199,10 +211,10 @@ print_device_notify(struct libinput_event *ev) devnode = udev_device_get_devnode( libinput_device_get_udev_device(dev)); - printf("Device: %s\n" - "Kernel: %s\n" - "Group: %d\n" - "Seat: %s, %s\n", + printf("Device: %s\n" + "Kernel: %s\n" + "Group: %d\n" + "Seat: %s, %s\n", libinput_device_get_name(dev), devnode, (int)group_id, @@ -210,8 +222,8 @@ print_device_notify(struct libinput_event *ev) libinput_seat_get_logical_name(seat)); if (libinput_device_get_size(dev, &w, &h) == 0) - printf("Size: %.2fx%.2fmm\n", w, h); - printf("Capabilities: "); + printf("Size: %.2fx%.2fmm\n", w, h); + printf("Capabilities: "); if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) printf("keyboard "); @@ -223,19 +235,20 @@ print_device_notify(struct libinput_event *ev) printf("touch"); printf("\n"); - printf("Tap-to-click: %s\n", tap_default(dev)); - printf("Left-handed: %s\n", left_handed_default(dev)); - printf("Nat.scrolling: %s\n", nat_scroll_default(dev)); + printf("Tap-to-click: %s\n", tap_default(dev)); + printf("Left-handed: %s\n", left_handed_default(dev)); + printf("Nat.scrolling: %s\n", nat_scroll_default(dev)); + printf("Middle emulation: %s\n", middle_emulation_default(dev)); str = calibration_default(dev); - printf("Calibration: %s\n", str); + printf("Calibration: %s\n", str); free(str); str = scroll_defaults(dev); - printf("Scroll methods: %s\n", str); + printf("Scroll methods: %s\n", str); free(str); str = click_defaults(dev); - printf("Click methods: %s\n", str); + printf("Click methods: %s\n", str); free(str); printf("\n"); From b97ad3c1741c484ccb1abac2cf27b8a1f11e354e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 08:09:50 +1000 Subject: [PATCH 126/165] test: add litest_add_ranged* functionality litest_add_ranged* takes a range parameter that serves as the lower/upper boundary for a loop. This enables tests to be run multiple times, avoiding the timeouts we triggered by having the loops inside (e.g. see 2bf8d035c and 6dd02468). This just wraps the underlying check framework, the ranged variable is available as "_i" in the test. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/litest.c | 64 +++++++++++++++++++++++++++++++++++++++++++-------- test/litest.h | 20 ++++++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/test/litest.c b/test/litest.c index c24a3061..2aac5a34 100644 --- a/test/litest.c +++ b/test/litest.c @@ -186,7 +186,8 @@ litest_drop_udev_rules(void) static void litest_add_tcase_for_device(struct suite *suite, void *func, - const struct litest_test_device *dev) + const struct litest_test_device *dev, + const struct range *range) { struct test *t; const char *test_name = dev->shortname; @@ -195,7 +196,13 @@ litest_add_tcase_for_device(struct suite *suite, if (strcmp(t->name, test_name) != 0) continue; - tcase_add_test(t->tc, func); + if (range) + tcase_add_loop_test(t->tc, + func, + range->lower, + range->upper); + else + tcase_add_test(t->tc, func); return; } @@ -218,7 +225,9 @@ litest_add_tcase_for_device(struct suite *suite, } static void -litest_add_tcase_no_device(struct suite *suite, void *func) +litest_add_tcase_no_device(struct suite *suite, + void *func, + const struct range *range) { struct test *t; const char *test_name = "no device"; @@ -227,7 +236,10 @@ litest_add_tcase_no_device(struct suite *suite, void *func) if (strcmp(t->name, test_name) != 0) continue; - tcase_add_test(t->tc, func); + if (range) + tcase_add_loop_test(t->tc, func, range->lower, range->upper); + else + tcase_add_test(t->tc, func); return; } @@ -243,7 +255,8 @@ litest_add_tcase_no_device(struct suite *suite, void *func) static void litest_add_tcase(struct suite *suite, void *func, enum litest_device_feature required, - enum litest_device_feature excluded) + enum litest_device_feature excluded, + const struct range *range) { struct litest_test_device **dev = devices; @@ -252,17 +265,17 @@ litest_add_tcase(struct suite *suite, void *func, if (required == LITEST_DISABLE_DEVICE && excluded == LITEST_DISABLE_DEVICE) { - litest_add_tcase_no_device(suite, func); + litest_add_tcase_no_device(suite, func, range); } else if (required != LITEST_ANY || excluded != LITEST_ANY) { while (*dev) { if (((*dev)->features & required) == required && ((*dev)->features & excluded) == 0) - litest_add_tcase_for_device(suite, func, *dev); + litest_add_tcase_for_device(suite, func, *dev, range); dev++; } } else { while (*dev) { - litest_add_tcase_for_device(suite, func, *dev); + litest_add_tcase_for_device(suite, func, *dev, range); dev++; } } @@ -274,6 +287,18 @@ litest_add_no_device(const char *name, void *func) litest_add(name, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE); } +void +litest_add_ranged_no_device(const char *name, + void *func, + const struct range *range) +{ + litest_add_ranged(name, + func, + LITEST_DISABLE_DEVICE, + LITEST_DISABLE_DEVICE, + range); +} + static struct suite * get_suite(const char *name) { @@ -304,13 +329,32 @@ litest_add(const char *name, enum litest_device_feature required, enum litest_device_feature excluded) { - litest_add_tcase(get_suite(name), func, required, excluded); + litest_add_ranged(name, func, required, excluded, NULL); +} + +void +litest_add_ranged(const char *name, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded, + const struct range *range) +{ + litest_add_tcase(get_suite(name), func, required, excluded, range); } void litest_add_for_device(const char *name, void *func, enum litest_device_type type) +{ + litest_add_ranged_for_device(name, func, type, NULL); +} + +void +litest_add_ranged_for_device(const char *name, + void *func, + enum litest_device_type type, + const struct range *range) { struct suite *s; struct litest_test_device **dev = devices; @@ -320,7 +364,7 @@ litest_add_for_device(const char *name, s = get_suite(name); while (*dev) { if ((*dev)->type == type) { - litest_add_tcase_for_device(s, func, *dev); + litest_add_tcase_for_device(s, func, *dev, range); return; } dev++; diff --git a/test/litest.h b/test/litest.h index 664b49ac..38d3c1b6 100644 --- a/test/litest.h +++ b/test/litest.h @@ -97,6 +97,14 @@ struct litest_device { char *udev_rule_file; }; +/* A loop range, resolves to: + for (i = lower; i < upper; i++) + */ +struct range { + int lower; /* inclusive */ + int upper; /* exclusive */ +}; + struct libinput *litest_create_context(void); void litest_disable_log_handler(struct libinput *libinput); void litest_restore_log_handler(struct libinput *libinput); @@ -104,11 +112,23 @@ void litest_restore_log_handler(struct libinput *libinput); void litest_add(const char *name, void *func, enum litest_device_feature required_feature, enum litest_device_feature excluded_feature); +void litest_add_ranged(const char *name, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded, + const struct range *range); void litest_add_for_device(const char *name, void *func, enum litest_device_type type); +void litest_add_ranged_for_device(const char *name, + void *func, + enum litest_device_type type, + const struct range *range); void litest_add_no_device(const char *name, void *func); +void litest_add_ranged_no_device(const char *name, + void *func, + const struct range *range); int litest_run(int argc, char **argv); struct litest_device * litest_create_device(enum litest_device_type which); From ca10fc2be734fc8f615ce7e0d15528f703ab79bd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 08:26:47 +1000 Subject: [PATCH 127/165] test: switch the abs ranged tests over to real ranged tests Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/device.c | 57 ++++++++++++--------------------------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/test/device.c b/test/device.c index 1c277ba4..b580e004 100644 --- a/test/device.c +++ b/test/device.c @@ -831,50 +831,22 @@ assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo) libevdev_uinput_destroy(uinput); } -START_TEST(abs_device_no_range_lo) +START_TEST(abs_device_no_range) { struct libinput *li; - int code; + int code = _i; /* looped test */ /* set x/y so libinput doesn't just reject for missing axes */ struct input_absinfo absinfo[] = { { ABS_X, 0, 10, 0, 0, 0 }, { ABS_Y, 0, 10, 0, 0, 0 }, - { -1, 0, 0, 0, 0, 0 }, + { code, 0, 0, 0, 0, 0 }, { -1, -1, -1, -1, -1, -1 } }; li = litest_create_context(); litest_disable_log_handler(li); - for (code = 0; code < ABS_MISC/2; code++) { - absinfo[2].value = code; - assert_device_ignored(li, absinfo); - } - - litest_restore_log_handler(li); - libinput_unref(li); -} -END_TEST - -START_TEST(abs_device_no_range_hi) -{ - struct libinput *li; - int code; - /* set x/y so libinput doesn't just reject for missing axes */ - struct input_absinfo absinfo[] = { - { ABS_X, 0, 10, 0, 0, 0 }, - { ABS_Y, 0, 10, 0, 0, 0 }, - { -1, 0, 0, 0, 0, 0 }, - { -1, -1, -1, -1, -1, -1 } - }; - - li = litest_create_context(); - litest_disable_log_handler(li); - - for (code = ABS_MISC/2; code < ABS_MISC; code++) { - absinfo[2].value = code; - assert_device_ignored(li, absinfo); - } + assert_device_ignored(li, absinfo); litest_restore_log_handler(li); libinput_unref(li); @@ -884,7 +856,7 @@ END_TEST START_TEST(abs_mt_device_no_range) { struct libinput *li; - int code; + int code = _i; /* looped test */ /* set x/y so libinput doesn't just reject for missing axes */ struct input_absinfo absinfo[] = { { ABS_X, 0, 10, 0, 0, 0 }, @@ -893,21 +865,16 @@ START_TEST(abs_mt_device_no_range) { ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 }, { ABS_MT_POSITION_X, 0, 10, 0, 0, 0 }, { ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 }, - { -1, 0, 0, 0, 0, 0 }, + { code, 0, 0, 0, 0, 0 }, { -1, -1, -1, -1, -1, -1 } }; li = litest_create_context(); litest_disable_log_handler(li); - for (code = ABS_MT_SLOT + 1; code < ABS_CNT; code++) { - if (code == ABS_MT_TOOL_TYPE || - code == ABS_MT_TRACKING_ID) /* kernel overrides it */ - continue; - - absinfo[6].value = code; + if (code != ABS_MT_TOOL_TYPE && + code != ABS_MT_TRACKING_ID) /* kernel overrides it */ assert_device_ignored(li, absinfo); - } litest_restore_log_handler(li); libinput_unref(li); @@ -978,6 +945,9 @@ END_TEST int main (int argc, char **argv) { + struct range abs_range = { 0, ABS_MISC }; + struct range abs_mt_range = { ABS_MT_SLOT + 1, ABS_CNT }; + litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD); litest_add("device:sendevents", device_sendevents_config_invalid, LITEST_ANY, LITEST_ANY); litest_add("device:sendevents", device_sendevents_config_touchpad, LITEST_TOUCHPAD, LITEST_ANY); @@ -1008,9 +978,8 @@ int main (int argc, char **argv) litest_add_no_device("device:invalid devices", abs_device_no_absy); litest_add_no_device("device:invalid devices", abs_mt_device_no_absx); litest_add_no_device("device:invalid devices", abs_mt_device_no_absy); - litest_add_no_device("device:invalid devices", abs_device_no_range_hi); - litest_add_no_device("device:invalid devices", abs_device_no_range_lo); - litest_add_no_device("device:invalid devices", abs_mt_device_no_range); + litest_add_ranged_no_device("device:invalid devices", abs_device_no_range, &abs_range); + litest_add_ranged_no_device("device:invalid devices", abs_mt_device_no_range, &abs_mt_range); litest_add_no_device("device:invalid devices", abs_device_missing_res); litest_add_no_device("device:invalid devices", abs_mt_device_missing_res); From 893fdf9b831772defdbd6ca34cd983a0975e0709 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 08:33:06 +1000 Subject: [PATCH 128/165] test: switch the touchpad multitap tests over to ranged tests Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- test/touchpad.c | 402 ++++++++++++++++++++++++------------------------ 1 file changed, 197 insertions(+), 205 deletions(-) diff --git a/test/touchpad.c b/test/touchpad.c index ad9e130c..2f7cb3c1 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -189,46 +189,44 @@ START_TEST(touchpad_1fg_multitap) struct libinput_event_pointer *ptrev; uint32_t oldtime = 0, curtime; - int i, ntaps; + int range = _i, /* looped test */ + ntaps; libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); - for (i = 3; i < 8; i++) { - - for (ntaps = 0; ntaps <= i; ntaps++) { - litest_touch_down(dev, 0, 50, 50); - litest_touch_up(dev, 0); - libinput_dispatch(li); - msleep(10); - } - - litest_timeout_tap(); + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); libinput_dispatch(li); - - for (ntaps = 0; ntaps <= i; ntaps++) { - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_gt(curtime, oldtime); - - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); - oldtime = curtime; - } - litest_timeout_tap(); - litest_assert_empty_queue(li); + msleep(10); } + + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + litest_timeout_tap(); + litest_assert_empty_queue(li); } END_TEST @@ -240,46 +238,27 @@ START_TEST(touchpad_1fg_multitap_n_drag_move) struct libinput_event_pointer *ptrev; uint32_t oldtime = 0, curtime; - int i, ntaps; + int range = _i, /* looped test */ + ntaps; libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); - for (i = 3; i < 8; i++) { - - for (ntaps = 0; ntaps <= i; ntaps++) { - litest_touch_down(dev, 0, 50, 50); - litest_touch_up(dev, 0); - libinput_dispatch(li); - msleep(10); - } - - libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); - litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + litest_touch_up(dev, 0); libinput_dispatch(li); + msleep(10); + } - for (ntaps = 0; ntaps <= i; ntaps++) { - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_gt(curtime, oldtime); - - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); - oldtime = curtime; - } + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { event = libinput_get_event(li); ptrev = litest_is_button_event(event, BTN_LEFT, @@ -288,16 +267,33 @@ START_TEST(touchpad_1fg_multitap_n_drag_move) libinput_event_destroy(event); ck_assert_int_gt(curtime, oldtime); - litest_assert_only_typed_events(li, - LIBINPUT_EVENT_POINTER_MOTION); - - litest_touch_up(dev, 0); - litest_assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - - litest_assert_empty_queue(li); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); } END_TEST @@ -309,47 +305,28 @@ START_TEST(touchpad_1fg_multitap_n_drag_2fg) struct libinput_event_pointer *ptrev; uint32_t oldtime = 0, curtime; - int i, ntaps; + int range = _i, + ntaps; libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); - for (i = 3; i < 8; i++) { - - for (ntaps = 0; ntaps <= i; ntaps++) { - litest_touch_down(dev, 0, 50, 50); - litest_touch_up(dev, 0); - libinput_dispatch(li); - msleep(10); - } - - libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); - msleep(10); - litest_touch_down(dev, 1, 70, 50); + litest_touch_up(dev, 0); libinput_dispatch(li); + msleep(10); + } - for (ntaps = 0; ntaps <= i; ntaps++) { - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_gt(curtime, oldtime); - - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); - oldtime = curtime; - } + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + msleep(10); + litest_touch_down(dev, 1, 70, 50); + libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { event = libinput_get_event(li); ptrev = litest_is_button_event(event, BTN_LEFT, @@ -358,20 +335,37 @@ START_TEST(touchpad_1fg_multitap_n_drag_2fg) libinput_event_destroy(event); ck_assert_int_gt(curtime, oldtime); - litest_touch_move_to(dev, 1, 70, 50, 90, 50, 10, 4); - - litest_assert_only_typed_events(li, - LIBINPUT_EVENT_POINTER_MOTION); - - litest_touch_up(dev, 1); - litest_touch_up(dev, 0); - litest_timeout_tap(); - litest_assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - - litest_assert_empty_queue(li); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 1, 70, 50, 90, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 1); + litest_touch_up(dev, 0); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); } END_TEST @@ -383,58 +377,56 @@ START_TEST(touchpad_1fg_multitap_n_drag_click) struct libinput_event_pointer *ptrev; uint32_t oldtime = 0, curtime; - int i, ntaps; + int range = _i, /* looped test */ + ntaps; libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); - for (i = 3; i < 5; i++) { - - for (ntaps = 0; ntaps <= i; ntaps++) { - litest_touch_down(dev, 0, 50, 50); - litest_touch_up(dev, 0); - libinput_dispatch(li); - msleep(10); - } - + for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); - libinput_dispatch(li); - litest_button_click(dev, BTN_LEFT, true); - litest_button_click(dev, BTN_LEFT, false); - libinput_dispatch(li); - - for (ntaps = 0; ntaps <= i; ntaps++) { - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_gt(curtime, oldtime); - - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); - oldtime = curtime; - } - - litest_assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - litest_assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); litest_touch_up(dev, 0); - litest_timeout_tap(); - - litest_assert_empty_queue(li); + libinput_dispatch(li); + msleep(10); } + + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_touch_up(dev, 0); + litest_timeout_tap(); + + litest_assert_empty_queue(li); } END_TEST @@ -446,48 +438,29 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) struct libinput_event_pointer *ptrev; uint32_t oldtime = 0, curtime; - int i, ntaps; + int range = _i, /* looped test */ + ntaps; libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); litest_drain_events(li); - for (i = 3; i < 5; i++) { - - for (ntaps = 0; ntaps <= i; ntaps++) { - litest_touch_down(dev, 0, 50, 50); - litest_touch_up(dev, 0); - libinput_dispatch(li); - msleep(10); - } - - libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); libinput_dispatch(li); + msleep(10); + } - litest_timeout_tap(); - libinput_dispatch(li); + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); - for (ntaps = 0; ntaps <= i; ntaps++) { - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_PRESSED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_gt(curtime, oldtime); - - event = libinput_get_event(li); - ptrev = litest_is_button_event(event, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - curtime = libinput_event_pointer_get_time(ptrev); - libinput_event_destroy(event); - ck_assert_int_ge(curtime, oldtime); - oldtime = curtime; - } + litest_timeout_tap(); + libinput_dispatch(li); + for (ntaps = 0; ntaps <= range; ntaps++) { event = libinput_get_event(li); ptrev = litest_is_button_event(event, BTN_LEFT, @@ -496,18 +469,35 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) libinput_event_destroy(event); ck_assert_int_gt(curtime, oldtime); - litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); - - litest_assert_only_typed_events(li, - LIBINPUT_EVENT_POINTER_MOTION); - - litest_touch_up(dev, 0); - litest_assert_button_event(li, - BTN_LEFT, - LIBINPUT_BUTTON_STATE_RELEASED); - - litest_assert_empty_queue(li); + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); } END_TEST @@ -4284,16 +4274,18 @@ END_TEST int main(int argc, char **argv) { + struct range multitap_range = {3, 8}; + litest_add("touchpad:motion", touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:tap", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY); - litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); - litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY); + litest_add_ranged("touchpad:tap", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &multitap_range); + litest_add_ranged("touchpad:tap", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY, &multitap_range); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); From 217ab899cf4f14945d794d559629766cee206486 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 6 May 2015 09:20:44 +1000 Subject: [PATCH 129/165] timer: fix coverity warning about unused return value "read(int, void *, size_t)" returns the number of bytes read, but it is ignored. We don't really care about the number of bytes, but let's complain if we get anything but EAGAIN. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/timer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/timer.c b/src/timer.c index 114a6496..d1d3c108 100644 --- a/src/timer.c +++ b/src/timer.c @@ -102,8 +102,14 @@ libinput_timer_handler(void *data) struct libinput_timer *timer, *tmp; uint64_t now; uint64_t discard; + int r; - read(libinput->timer.fd, &discard, sizeof(discard)); + r = read(libinput->timer.fd, &discard, sizeof(discard)); + if (r == -1 && errno != EAGAIN) + log_bug_libinput(libinput, + "Error %d reading from timerfd (%s)", + errno, + strerror(errno)); now = libinput_now(libinput); if (now == 0) From 1ab385bc83504272cf4507d72ac0d6ab448aac9c Mon Sep 17 00:00:00 2001 From: Velimir Lisec Date: Thu, 30 Apr 2015 00:13:51 +0200 Subject: [PATCH 130/165] touchpad: end tap-and-drag with an extra tap Currently for the tap-and-drag gesture to end user has to wait for a timeout to expire. Make it possible to end the drag gesture by just tapping. The allowed finger sequences to start and end a drag are thus: tap, down, .... move ...., up tap, down, .... move ...., up, tap https://bugs.freedesktop.org/show_bug.cgi?id=90255 Signed-off-by: Velimir Lisec Reviewed-by: Peter Hutterer State diagram changes and a doc change squashed in. Signed-off-by: Peter Hutterer --- doc/tapping.dox | 4 + doc/touchpad-tap-state-machine.svg | 479 ++++++++++++++++------------- src/evdev-mt-touchpad-tap.c | 36 ++- src/evdev-mt-touchpad.h | 1 + 4 files changed, 302 insertions(+), 218 deletions(-) diff --git a/doc/tapping.dox b/doc/tapping.dox index f603b7d5..7eb81e62 100644 --- a/doc/tapping.dox +++ b/doc/tapping.dox @@ -33,6 +33,10 @@ continue the dragging process, so that multiple touchpad-widths of distance can be covered easily. If two-fingers are supported by the hardware, a second finger can be used to drag while the first is held in-place. +An alternative method to end a drag process is to tap immediately after +lifting the finger. The full sequence is thus: tap, finger down, drag, +finger up, tap. + @section tap_constraints Constraints while tapping A couple of constraints apply to the contact to be converted into a press, the most common ones are: diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index b168b4f6..39b0b86b 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -14,93 +14,93 @@ - IDLE + IDLE - TOUCH + TOUCH - first + first - finger down + finger down - finger up + finger up - button 1 + button 1 - press + press - timeout + timeout - move > + move > - threshold + threshold - second + second - finger down + finger down - TOUCH_2 + TOUCH_2 - second + second - finger up + finger up - button 2 + button 2 - press + press - move > + move > - threshold + threshold - timeout + timeout @@ -109,16 +109,16 @@ - button 1 + button 1 - release + release - button 2 + button 2 - release + release @@ -127,42 +127,42 @@ - TAPPED + TAPPED - timeout + timeout - first + first - finger down + finger down - DRAGGING + DRAGGING - first + first - finger up + finger up - btn1 + btn1 - release + release @@ -173,81 +173,81 @@ - IDLE + IDLE - third + third - finger down + finger down - TOUCH_3 + TOUCH_3 - button 3 + button 3 - press + press - button 3 + button 3 - release + release - move > + move > - threshold + threshold - IDLE + IDLE - timeout + timeout - first + first - finger up + finger up - IDLE + IDLE - fourth + fourth - finger down + finger down @@ -256,64 +256,64 @@ - DRAGGING_OR_DOUBLETAP + DRAGGING_OR_DOUBLETAP - timeout + timeout - first + first - finger up + finger up - button 1 + button 1 - release + release - button 1 + button 1 - press + press - btn1 + btn1 - release + release - second + second - finger down + finger down - move > + move > - threshold + threshold @@ -322,14 +322,14 @@ - HOLD + HOLD - first + first - finger up + finger up @@ -338,9 +338,9 @@ - second + second - finger down + finger down @@ -351,23 +351,23 @@ - TOUCH_2_HOLD + TOUCH_2_HOLD - second + second - finger up + finger up - first + first - finger up + finger up @@ -378,9 +378,9 @@ - third + third - finger down + finger down @@ -391,21 +391,21 @@ - TOUCH_3_HOLD + TOUCH_3_HOLD - fourth + fourth - finger down + finger down - DEAD + DEAD @@ -416,19 +416,19 @@ - any finger up + any finger up - fourth + fourth - finger up + finger up - any finger up + any finger up @@ -437,12 +437,12 @@ - yes + yes - any finger up + any finger up @@ -455,14 +455,14 @@ - IDLE + IDLE - if finger + if finger - count == 0 + count == 0 @@ -473,14 +473,14 @@ - second + second - finger up + finger up - DRAGGING_2 + DRAGGING_2 @@ -489,9 +489,9 @@ - first + first - finger up + finger up @@ -502,9 +502,9 @@ - second + second - finger down + finger down @@ -515,29 +515,29 @@ - third + third - finger down + finger down - btn1 + btn1 - release + release - phys + phys - button + button - press + press @@ -554,23 +554,23 @@ - phys + phys - button + button - press + press - + - - button 1 - - release + + button 1 + + release - - + + @@ -582,12 +582,12 @@ - DRAGGING_WAIT + DRAGGING_WAIT - timeout + timeout @@ -598,21 +598,21 @@ - first + first - finger down + finger down - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_IDLE + TOUCH_IDLE @@ -625,7 +625,7 @@ - TOUCH_DEAD + TOUCH_DEAD @@ -640,12 +640,12 @@ - yes + yes - TOUCH_DEAD + TOUCH_DEAD @@ -656,14 +656,14 @@ - TOUCH_IDLE + TOUCH_IDLE - TOUCH_TOUCH + TOUCH_TOUCH @@ -672,44 +672,44 @@ - TOUCH_IDLE + TOUCH_IDLE - TOUCH_IDLE + TOUCH_IDLE - TOUCH_IDLE + TOUCH_IDLE - TOUCH_TOUCH + TOUCH_TOUCH - that finger + that finger - TOUCH_IDLE + TOUCH_IDLE - TOUCH_DEAD + TOUCH_DEAD @@ -720,9 +720,9 @@ - that finger + that finger - TOUCH_IDLE + TOUCH_IDLE @@ -731,31 +731,31 @@ - no + no - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_IDLE + TOUCH_IDLE - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_DEAD + TOUCH_DEAD @@ -764,175 +764,171 @@ - TOUCH_IDLE + TOUCH_IDLE - + - - TOUCH_TOUCH + + TOUCH_TOUCH - - - - - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_IDLE + TOUCH_IDLE - TOUCH_IDLE + TOUCH_IDLE - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_IDLE + TOUCH_IDLE - TOUCH_TOUCH + TOUCH_TOUCH - that finger + that finger - TOUCH_IDLE + TOUCH_IDLE - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - - + + - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - state == + state == - TOUCH_TOUCH + TOUCH_TOUCH - that finger state == + that finger state == - TOUCH_TOUCH + TOUCH_TOUCH - no + no - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - TOUCH_DEAD + TOUCH_DEAD - first + first - finger down + finger down - + - - MULTITAP + + MULTITAP - - - - + + + + - timeout + timeout - - + + - IDLE + IDLE @@ -943,14 +939,14 @@ - MULTITAP_DOWN + MULTITAP_DOWN - button 1 + button 1 - press + press @@ -959,39 +955,39 @@ - first + first - finger up + finger up - button 1 + button 1 - release + release - timeout + timeout - second + second - finger down + finger down - move > + move > - threshold + threshold @@ -1002,16 +998,16 @@ - button 1 + button 1 - release + release - button 1 + button 1 - press + press @@ -1022,16 +1018,16 @@ - button 1 + button 1 - release + release - button 1 + button 1 - press + press @@ -1042,16 +1038,16 @@ - button 1 + button 1 - release + release - button 1 + button 1 - press + press @@ -1059,36 +1055,87 @@ - - + + - TOUCH_TOUCH + TOUCH_TOUCH - TOUCH_IDLE + TOUCH_IDLE - phys + phys - button + button - press + press - - + + + + + + DRAGGING_OR_TAP + + + + + first + + finger up + + + + + + + timeout + + + + + + + + + move > + + threshold + + + + + + + + + + + + + + + TOUCH_IDLE + + + + + + + diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 61f94e51..fb8c9e43 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -74,6 +74,7 @@ tap_state_to_str(enum tp_tap_state state) CASE_RETURN_STRING(TAP_STATE_DRAGGING); CASE_RETURN_STRING(TAP_STATE_DRAGGING_WAIT); CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_DOUBLETAP); + CASE_RETURN_STRING(TAP_STATE_DRAGGING_OR_TAP); CASE_RETURN_STRING(TAP_STATE_DRAGGING_2); CASE_RETURN_STRING(TAP_STATE_MULTITAP); CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN); @@ -409,8 +410,8 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_DRAGGING; - tp_tap_clear_timer(tp); + tp->tap.state = TAP_STATE_DRAGGING_OR_TAP; + tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: case TAP_EVENT_MOTION: @@ -426,6 +427,32 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, } } +static void +tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + tp->tap.state = TAP_STATE_DRAGGING_2; + tp_tap_clear_timer(tp); + break; + case TAP_EVENT_RELEASE: + tp->tap.state = TAP_STATE_IDLE; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + break; + case TAP_EVENT_MOTION: + case TAP_EVENT_TIMEOUT: + tp->tap.state = TAP_STATE_DRAGGING; + break; + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_DEAD; + tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); + break; + } +} + static void tp_tap_dragging2_handle_event(struct tp_dispatch *tp, struct tp_touch *t, @@ -588,6 +615,9 @@ tp_tap_handle_event(struct tp_dispatch *tp, case TAP_STATE_DRAGGING_WAIT: tp_tap_dragging_wait_handle_event(tp, t, event, time); break; + case TAP_STATE_DRAGGING_OR_TAP: + tp_tap_dragging_tap_handle_event(tp, t, event, time); + break; case TAP_STATE_DRAGGING_2: tp_tap_dragging2_handle_event(tp, t, event, time); break; @@ -692,6 +722,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) case TAP_STATE_TOUCH: case TAP_STATE_TAPPED: case TAP_STATE_DRAGGING_OR_DOUBLETAP: + case TAP_STATE_DRAGGING_OR_TAP: case TAP_STATE_TOUCH_2: case TAP_STATE_TOUCH_3: case TAP_STATE_MULTITAP_DOWN: @@ -870,6 +901,7 @@ tp_tap_dragging(struct tp_dispatch *tp) case TAP_STATE_DRAGGING: case TAP_STATE_DRAGGING_2: case TAP_STATE_DRAGGING_WAIT: + case TAP_STATE_DRAGGING_OR_TAP: return true; default: return false; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 37113752..f602359f 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -94,6 +94,7 @@ enum tp_tap_state { TAP_STATE_TOUCH_3, TAP_STATE_TOUCH_3_HOLD, TAP_STATE_DRAGGING_OR_DOUBLETAP, + TAP_STATE_DRAGGING_OR_TAP, TAP_STATE_DRAGGING, TAP_STATE_DRAGGING_WAIT, TAP_STATE_DRAGGING_2, From 2e0f45b5373ffbf80b5b8c417e6a2065da1020b0 Mon Sep 17 00:00:00 2001 From: Velimir Lisec Date: Wed, 6 May 2015 08:42:37 +0200 Subject: [PATCH 131/165] test: add test cases for ending drag with a tap Signed-off-by: Velimir Lisec Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- test/touchpad.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/test/touchpad.c b/test/touchpad.c index ad9e130c..3d37f9e2 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -511,6 +511,168 @@ START_TEST(touchpad_1fg_multitap_n_drag_timeout) } END_TEST +START_TEST(touchpad_1fg_multitap_n_drag_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 5; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 70, 50); + litest_touch_up(dev, 0); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); + } +} +END_TEST + +START_TEST(touchpad_1fg_multitap_n_drag_tap_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + uint32_t oldtime = 0, + curtime; + int i, ntaps; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + for (i = 3; i < 5; i++) { + + for (ntaps = 0; ntaps <= i; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + libinput_dispatch(li); + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= i; ntaps++) { + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_ge(curtime, oldtime); + oldtime = curtime; + } + + event = libinput_get_event(li); + ptrev = litest_is_button_event(event, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + curtime = libinput_event_pointer_get_time(ptrev); + libinput_event_destroy(event); + ck_assert_int_gt(curtime, oldtime); + + litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 4); + + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 70, 50); + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + /* the physical click */ + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); + } +} +END_TEST + START_TEST(touchpad_1fg_tap_n_drag) { struct litest_device *dev = litest_current_device(); @@ -552,6 +714,101 @@ START_TEST(touchpad_1fg_tap_n_drag) } END_TEST +START_TEST(touchpad_1fg_tap_n_drag_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + libinput_dispatch(li); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + /* lift finger, set down again, should continue dragging */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_1fg_tap_n_drag_tap_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_ENABLED); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + libinput_dispatch(li); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + /* lift finger, set down again, should continue dragging */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 80, 80, 5, 40); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_touch_up(dev, 0); + litest_touch_down(dev, 0, 50, 50); + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + litest_assert_button_event(li, BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + /* the physical click */ + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_touch_up(dev, 0); + + litest_assert_empty_queue(li); +} +END_TEST + START_TEST(touchpad_1fg_tap_n_drag_timeout) { struct litest_device *dev = litest_current_device(); @@ -4291,10 +4548,12 @@ int main(int argc, char **argv) litest_add("touchpad:tap", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_multitap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_move, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_timeout, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_2fg_tap_n_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:tap", touchpad_2fg_tap_n_drag_3fg_btntool, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD); @@ -4323,6 +4582,8 @@ int main(int argc, char **argv) pads with buttons */ litest_add("touchpad:tap", touchpad_1fg_double_tap_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_click, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_multitap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY); + litest_add("touchpad:tap", touchpad_1fg_tap_n_drag_tap_click, LITEST_CLICKPAD, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_default_disabled, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_ANY); litest_add("touchpad:tap", touchpad_tap_default_enabled, LITEST_TOUCHPAD, LITEST_BUTTON); From d288eb0a63793ebb64de041960336d6fb57060b0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Apr 2015 16:09:31 +1000 Subject: [PATCH 132/165] touchpad: switch from is_palm to an enum Preparation to add different palm detection types. Not all of them need to be un-done when leaving the edge area so a boolean is not enough. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 10 +++++----- src/evdev-mt-touchpad.h | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 79177fb0..d8b44fa6 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -231,7 +231,7 @@ tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) } t->dirty = true; - t->palm.is_palm = false; + t->palm.state = PALM_NONE; t->state = TOUCH_END; t->pinned.is_pinned = false; t->millis = time; @@ -455,7 +455,7 @@ int tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t) { return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) && - !t->palm.is_palm && + t->palm.state == PALM_NONE && !t->pinned.is_pinned && tp_button_touch_active(tp, t) && tp_edge_scroll_touch_active(tp, t); @@ -491,14 +491,14 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) we move out of the palm edge zone within the timeout, provided the direction is within 45 degrees of the horizontal. */ - if (t->palm.is_palm) { + if (t->palm.state == PALM_EDGE) { if (time < t->palm.time + PALM_TIMEOUT && (t->point.x > tp->palm.left_edge && t->point.x < tp->palm.right_edge)) { delta = device_delta(t->point, t->palm.first); dirs = normalized_get_direction( tp_normalize_delta(tp, delta)); if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) { - t->palm.is_palm = false; + t->palm.state = PALM_NONE; } } return; @@ -517,7 +517,7 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) tp_button_is_inside_softbutton_area(tp, t)) return; - t->palm.is_palm = true; + t->palm.state = PALM_EDGE; t->palm.time = time; t->palm.first = t->point; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index f602359f..ba65e413 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -61,6 +61,11 @@ enum touch_state { TOUCH_END }; +enum touch_palm_state { + PALM_NONE = 0, + PALM_EDGE, +}; + enum button_event { BUTTON_EVENT_IN_BOTTOM_R = 30, BUTTON_EVENT_IN_BOTTOM_L, @@ -171,7 +176,7 @@ struct tp_touch { } scroll; struct { - bool is_palm; + enum touch_palm_state state; struct device_coords first; /* first coordinates if is_palm == true */ uint32_t time; /* first timestamp if is_palm == true */ } palm; From d02b670e3796c10fa75b94165886901a0a0c41f4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Apr 2015 15:51:20 +1000 Subject: [PATCH 133/165] touchpad: add timeout-based disable-while-typing On some touchpads, typing triggers touches in areas of the touchpad that cannot easily be distinguished from other fingers. Pressure information is useless too, so we have to go back to a timeout-based handling of touch data. If we see non-modifier key events, disable the touchpad for a timeout and set any touches starting during that timeout as palm. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 90 +++++++++++++++++++++++++++++++++++++++-- src/evdev-mt-touchpad.h | 7 ++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index d8b44fa6..32d8e25e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -34,6 +34,7 @@ #define DEFAULT_ACCEL_NUMERATOR 3000.0 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */ +#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT 500 /* ms */ #define FAKE_FINGER_OVERFLOW (1 << 7) static inline int @@ -487,6 +488,14 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) struct device_float_coords delta; int dirs; + if (tp->sendevents.keyboard_active && + t->state == TOUCH_BEGIN) { + t->palm.state = PALM_TYPING; + t->palm.time = time; + t->palm.first = t->point; + return; + } + /* If labelled a touch as palm, we unlabel as palm when we move out of the palm edge zone within the timeout, provided the direction is within 45 degrees of the horizontal. @@ -705,7 +714,9 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time) filter_motion |= tp_tap_handle_state(tp, time); filter_motion |= tp_post_button_events(tp, time); - if (filter_motion || tp->sendevents.trackpoint_active) { + if (filter_motion || + tp->sendevents.trackpoint_active || + tp->sendevents.keyboard_active) { tp_edge_scroll_stop_events(tp, time); tp_gesture_stop(tp, time); return; @@ -755,10 +766,15 @@ static void tp_remove_sendevents(struct tp_dispatch *tp) { libinput_timer_cancel(&tp->sendevents.trackpoint_timer); + libinput_timer_cancel(&tp->sendevents.keyboard_timer); if (tp->buttons.trackpoint) libinput_device_remove_event_listener( &tp->sendevents.trackpoint_listener); + + if (tp->sendevents.keyboard) + libinput_device_remove_event_listener( + &tp->sendevents.keyboard_listener); } static void @@ -880,14 +896,60 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data) time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT); } +static void +tp_keyboard_timeout(uint64_t now, void *data) +{ + struct tp_dispatch *tp = data; + + tp_tap_resume(tp, now); + tp->sendevents.keyboard_active = false; +} + +static void +tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) +{ + struct tp_dispatch *tp = data; + struct libinput_event_keyboard *kbdev; + + if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) + return; + + kbdev = libinput_event_get_keyboard_event(event); + /* modifier keys don't trigger disable-while-typing so things like + * ctrl+zoom or ctrl+click are possible */ + switch (libinput_event_keyboard_get_key(kbdev)) { + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + case KEY_LEFTALT: + case KEY_RIGHTALT: + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + case KEY_FN: + return; + default: + break; + } + + if (!tp->sendevents.keyboard_active) { + tp_edge_scroll_stop_events(tp, time); + tp_gesture_stop(tp, time); + tp_tap_suspend(tp, time); + tp->sendevents.keyboard_active = true; + } + + libinput_timer_set(&tp->sendevents.keyboard_timer, + time + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT); +} + static void tp_device_added(struct evdev_device *device, struct evdev_device *added_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; unsigned int bus_tp = libevdev_get_id_bustype(device->evdev), - bus_trp = libevdev_get_id_bustype(added_device->evdev); - bool tp_is_internal, trp_is_internal; + bus_trp = libevdev_get_id_bustype(added_device->evdev), + bus_kbd = libevdev_get_id_bustype(added_device->evdev); + bool tp_is_internal, trp_is_internal, kbd_is_internal; tp_is_internal = bus_tp != BUS_USB && bus_tp != BUS_BLUETOOTH; trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH; @@ -903,6 +965,18 @@ tp_device_added(struct evdev_device *device, tp_trackpoint_event, tp); } + /* FIXME: detect external keyboard better */ + kbd_is_internal = bus_tp != BUS_BLUETOOTH && + bus_kbd == bus_tp; + if (tp_is_internal && kbd_is_internal && + tp->sendevents.keyboard == NULL) { + libinput_device_add_event_listener(&added_device->base, + &tp->sendevents.keyboard_listener, + tp_keyboard_event, tp); + tp->sendevents.keyboard = added_device; + tp->sendevents.keyboard_active = false; + } + if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) return; @@ -929,6 +1003,12 @@ tp_device_removed(struct evdev_device *device, tp->buttons.trackpoint = NULL; } + if (removed_device == tp->sendevents.keyboard) { + libinput_device_remove_event_listener( + &tp->sendevents.keyboard_listener); + tp->sendevents.keyboard = NULL; + } + if (tp->sendevents.current_mode != LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) return; @@ -1204,6 +1284,10 @@ tp_init_sendevents(struct tp_dispatch *tp, libinput_timer_init(&tp->sendevents.trackpoint_timer, tp->device->base.seat->libinput, tp_trackpoint_timeout, tp); + + libinput_timer_init(&tp->sendevents.keyboard_timer, + tp->device->base.seat->libinput, + tp_keyboard_timeout, tp); return 0; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index ba65e413..3d51a398 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -64,6 +64,7 @@ enum touch_state { enum touch_palm_state { PALM_NONE = 0, PALM_EDGE, + PALM_TYPING, }; enum button_event { @@ -277,9 +278,15 @@ struct tp_dispatch { struct { struct libinput_device_config_send_events config; enum libinput_config_send_events_mode current_mode; + bool trackpoint_active; struct libinput_event_listener trackpoint_listener; struct libinput_timer trackpoint_timer; + + bool keyboard_active; + struct libinput_event_listener keyboard_listener; + struct libinput_timer keyboard_timer; + struct evdev_device *keyboard; } sendevents; }; From c2f8b508b9fc661967516ca07d2100ca8749c101 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 20 Apr 2015 16:20:00 +1000 Subject: [PATCH 134/165] touchpad: use a two-stage timeout for disable-while-typing Hitting a single key triggers a short timeout, just enough to cover the time to the next key event. Hitting more than one key triggers the longer timeout. This should improve responsiveness after single key events when the touchpad is still the main interaction mode and a key needs to be pressed to advance in the UI. When typing the hands require physical movement to get back to the touchpad anyway so a longer timeout is acceptable and more reliable. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 32d8e25e..3ebb5591 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -34,7 +34,8 @@ #define DEFAULT_ACCEL_NUMERATOR 3000.0 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */ -#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT 500 /* ms */ +#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 200 /* ms */ +#define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2 500 /* ms */ #define FAKE_FINGER_OVERFLOW (1 << 7) static inline int @@ -910,11 +911,18 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) { struct tp_dispatch *tp = data; struct libinput_event_keyboard *kbdev; + unsigned int timeout; if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) return; kbdev = libinput_event_get_keyboard_event(event); + + /* Only trigger the timer on key down. */ + if (libinput_event_keyboard_get_key_state(kbdev) != + LIBINPUT_KEY_STATE_PRESSED) + return; + /* modifier keys don't trigger disable-while-typing so things like * ctrl+zoom or ctrl+click are possible */ switch (libinput_event_keyboard_get_key(kbdev)) { @@ -935,10 +943,13 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) tp_gesture_stop(tp, time); tp_tap_suspend(tp, time); tp->sendevents.keyboard_active = true; + timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1; + } else { + timeout = DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2; } libinput_timer_set(&tp->sendevents.keyboard_timer, - time + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT); + time + timeout); } static void From 6acd97457fa0d496b3b72c1a969e5b41cefca2bc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 18 May 2015 14:54:50 +1000 Subject: [PATCH 135/165] doc: add some documentation for disable-while-typing Signed-off-by: Peter Hutterer --- doc/palm-detection.dox | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/palm-detection.dox b/doc/palm-detection.dox index a5b578ba..a03f9c14 100644 --- a/doc/palm-detection.dox +++ b/doc/palm-detection.dox @@ -57,5 +57,26 @@ the palm on the touchpad while using the trackstick). If the touchpad is disabled, the @ref t440_support "top software buttons" remain enabled. +@section disable-while-typing Disable-while-typing + +libinput automatically disables the touchpad for a timeout after a key +press, a feature traditionally referred to as "disable while typing" and +previously available through the +[syndaemon(1)](http://linux.die.net/man/1/syndaemon) command. libinput does +not require an external command and the feature is currently enabled for all +touchpads but will be reduced in the future to only apply to touchpads where +finger width or pressure data is unreliable. + +Notable behaviors of libinput's disable-while-typing feature: +- Two different timeouts are used, after a single key press the timeout is + short to ensure responsiveness. After multiple key events, the timeout is + longer to avoid accidental pointer manipulation while typing. +- Some keys do not trigger the timeout, specifically some modifier keys + (Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay + responsive. +- Touches started while the touchpad is disabled do not control the cursor, + it is thus possible to rest the palm on the touchpad while typing. +- Physical buttons work even while the touchpad is disabled. This includes + software-emulated buttons. */ From 7b965b63c03015ed350d1308277868b7e4644ca4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 11:27:25 +1000 Subject: [PATCH 136/165] test: type-safe two ck_assert invocations unsigned vs signed in both cases, but the ck_assert macros hide that (everything is cast to intmax_t). The upcoming litest_assert wrappers expose these warnings, so fix them ahead of time. Signed-off-by: Peter Hutterer --- test/litest.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/litest.c b/test/litest.c index 2aac5a34..b755d0de 100644 --- a/test/litest.c +++ b/test/litest.c @@ -611,9 +611,10 @@ litest_init_udev_rules(struct litest_test_device *dev) UDEV_RULE_PREFIX, dev->shortname); ck_assert_int_eq(rc, + (int)( strlen(UDEV_RULES_D) + strlen(UDEV_RULE_PREFIX) + - strlen(dev->shortname) + 7); + strlen(dev->shortname) + 7)); f = fopen(path, "w"); ck_assert_notnull(f); ck_assert_int_ge(fputs(dev->udev_rule, f), 0); @@ -1170,7 +1171,7 @@ litest_scale(const struct litest_device *d, unsigned int axis, double val) int min, max; ck_assert_int_ge(val, 0); ck_assert_int_le(val, 100); - ck_assert_int_le(axis, ABS_Y); + ck_assert_int_le(axis, (unsigned int)ABS_Y); min = d->interface->min[axis]; max = d->interface->max[axis]; From d81a677f6dc921ddebbc446b06e92c69a0699a37 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 11:29:49 +1000 Subject: [PATCH 137/165] test: don't use enum values in ck_assert macros directly Unfortunately, typeof(enum something) != typeof(ENUM_VALUE) and produces a -Wsign-compare warning Preemptively fix this, it'll show up in the upcoming litest_asssert macros otherwise. This fix only applies to helper functions, tests themselves wont (yet) be switched to the new macros and don't need fixing. Signed-off-by: Peter Hutterer --- test/litest.c | 4 ++-- test/pointer.c | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/litest.c b/test/litest.c index b755d0de..24a25de0 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1506,10 +1506,10 @@ litest_is_button_event(struct libinput_event *event, enum libinput_button_state state) { struct libinput_event_pointer *ptrev; + enum libinput_event_type type = LIBINPUT_EVENT_POINTER_BUTTON; ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_BUTTON); + ck_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); ck_assert_int_eq(libinput_event_pointer_get_button(ptrev), button); diff --git a/test/pointer.c b/test/pointer.c index 43c21ba3..4b93c25d 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -105,12 +105,14 @@ static void disable_button_scrolling(struct litest_device *device) { struct libinput_device *dev = device->libinput_device; - enum libinput_config_status status; + enum libinput_config_status status, + expected; status = libinput_device_config_scroll_set_method(dev, LIBINPUT_CONFIG_SCROLL_NO_SCROLL); - ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + expected = LIBINPUT_CONFIG_STATUS_SUCCESS; + ck_assert_int_eq(status, expected); } START_TEST(pointer_motion_relative) @@ -138,14 +140,14 @@ test_absolute_event(struct litest_device *dev, double x, double y) struct libinput_event *event; struct libinput_event_pointer *ptrev; double ex, ey; + enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE; litest_touch_down(dev, 0, x, y); libinput_dispatch(li); event = libinput_get_event(li); ck_assert_notnull(event); - ck_assert_int_eq(libinput_event_get_type(event), - LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); + ck_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); ck_assert(ptrev != NULL); From c9ea9836f3d64ac35b0e5be42b4217efcfd7c2af Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 May 2015 11:52:32 +1000 Subject: [PATCH 138/165] test: cast to int before integer comparison No effect with the current macros since they'll cast to int for us, but this will change with the litest_assert macros. Signed-off-by: Peter Hutterer --- test/litest.c | 17 +++++++---------- test/pointer.c | 4 ++-- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/test/litest.c b/test/litest.c index 24a25de0..aae2663e 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1169,8 +1169,8 @@ int litest_scale(const struct litest_device *d, unsigned int axis, double val) { int min, max; - ck_assert_int_ge(val, 0); - ck_assert_int_le(val, 100); + ck_assert_int_ge((int)val, 0); + ck_assert_int_le((int)val, 100); ck_assert_int_le(axis, (unsigned int)ABS_Y); min = d->interface->min[axis]; @@ -1630,6 +1630,7 @@ litest_assert_scroll(struct libinput *li, { struct libinput_event *event, *next_event; struct libinput_event_pointer *ptrev; + int value; event = libinput_get_event(li); next_event = libinput_get_event(li); @@ -1639,17 +1640,13 @@ litest_assert_scroll(struct libinput *li, ptrev = litest_is_axis_event(event, axis, 0); if (next_event) { + value = libinput_event_pointer_get_axis_value(ptrev, + axis); /* Normal scroll event, check dir */ if (minimum_movement > 0) { - ck_assert_int_ge( - libinput_event_pointer_get_axis_value(ptrev, - axis), - minimum_movement); + ck_assert_int_ge(value, minimum_movement); } else { - ck_assert_int_le( - libinput_event_pointer_get_axis_value(ptrev, - axis), - minimum_movement); + ck_assert_int_le(value, minimum_movement); } } else { /* Last scroll event, must be 0 */ diff --git a/test/pointer.c b/test/pointer.c index 4b93c25d..a47b95c1 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -154,8 +154,8 @@ test_absolute_event(struct litest_device *dev, double x, double y) ex = libinput_event_pointer_get_absolute_x_transformed(ptrev, 100); ey = libinput_event_pointer_get_absolute_y_transformed(ptrev, 100); - ck_assert_int_eq(ex + 0.5, x); - ck_assert_int_eq(ey + 0.5, y); + ck_assert_int_eq((int)(ex + 0.5), (int)x); + ck_assert_int_eq((int)(ey + 0.5), (int)y); libinput_event_destroy(event); } From 84e6321f515f0683f90912a588d4bf7d21f7948f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 09:13:22 +1000 Subject: [PATCH 139/165] test: add a backtrace facility to litest The check unit test framework isn't particularly suited to having ck_assert* calls in helper functions. A failed assertion in a helper function or the litest framework merely gives us a the failed line in litest.c. which doesn't tell us which test actually failed. Add a backtracing facility with litest_backtrace(). And since this requires wrapping all ck_assert macros with litest_assert() this patch ended up replacing/duplicating a bunch of ck_assert_* bits. So rather than ck_assert_int_eq() we now use litest_assert_int_eq(), etc. in the litest framework itself. The int comparison macros are more type-safe than ck_assert()'s macros which just cast all the ints to intmax_t. Backtrace is spewed to stderr, which is good enough for debugging. Example backtrace: FAILED COMPARISON: status != expected Resolved to: 0 != 0 in disable_button_scrolling() (pointer.c:115) Backtrace: 0: ./test/test-pointer (litest_fail_comparison_int+0xab) [0x40973b] 1: ./test/test-pointer (disable_button_scrolling+0x174) [0x40421b] 2: ./test/test-pointer (middlebutton_middleclick+0x40) [0x40829c] 3: /lib64/libcheck.so.0 (srunner_run+0x7f5) [0x7f0e8a277025] 4: ./test/test-pointer (litest_run+0x107) [0x40a42b] 5: ./test/test-pointer (main+0x2fa) [0x4090e7] 6: /lib64/libc.so.6 (__libc_start_main+0xf0) [0x7f0e88f5e790] 7: ./test/test-pointer (_start+0x29) [0x403ce9] 8: ? (?+0x29) [0x29] litest_backtrace() itself is copied from xserver/os/backtrace.c which git blame attributes to Marcin. CC: Marcin Slusarz Signed-off-by: Peter Hutterer --- configure.ac | 10 ++ test/Makefile.am | 16 ++- test/litest-selftest.c | 228 +++++++++++++++++++++++++++++++ test/litest.c | 272 +++++++++++++++++++++++++++---------- test/litest.h | 73 ++++++++++ test/valgrind.suppressions | 8 ++ 6 files changed, 537 insertions(+), 70 deletions(-) create mode 100644 test/litest-selftest.c diff --git a/configure.ac b/configure.ac index cdde4fae..75951fda 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,15 @@ PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0]) PKG_CHECK_MODULES(LIBUDEV, [libudev]) PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4]) +PKG_CHECK_MODULES(LIBUNWIND, + [libunwind], + [HAVE_LIBUNWIND=yes], + [HAVE_LIBUNWIND=no]) +if test "x$HAVE_LIBUNWIND" = "xyes"; then + AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support]) +fi +AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes]) + AC_CHECK_LIB([m], [atan2]) AC_CHECK_LIB([rt], [clock_gettime]) @@ -190,5 +199,6 @@ AC_MSG_RESULT([ Build documentation ${build_documentation} Build tests ${build_tests} Tests use valgrind ${VALGRIND} + Tests use libunwind ${HAVE_LIBUNWIND} Build GUI event tool ${build_eventgui} ]) diff --git a/test/Makefile.am b/test/Makefile.am index 0df89b2c..a36d3681 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -38,6 +38,11 @@ liblitest_la_SOURCES = \ litest-vmware-virtual-usb-mouse.c \ litest.c liblitest_la_LIBADD = $(top_builddir)/src/libinput-util.la +liblitest_la_CFLAGS = $(AM_CFLAGS) +if HAVE_LIBUNWIND +liblitest_la_LIBADD += $(LIBUNWIND_LIBS) -ldl +liblitest_la_CFLAGS += $(LIBUNWIND_CFLAGS) +endif run_tests = \ test-touchpad \ @@ -49,7 +54,9 @@ run_tests = \ test-path \ test-log \ test-misc \ - test-keyboard + test-keyboard \ + test-litest-selftest + build_tests = \ test-build-cxx \ test-build-linker \ @@ -102,6 +109,11 @@ test_device_SOURCES = device.c test_device_LDADD = $(TEST_LIBS) test_device_LDFLAGS = -no-install +test_litest_selftest_SOURCES = litest-selftest.c litest.c litest-int.h litest.h +test_litest_selftest_CFLAGS = -DLITEST_DISABLE_BACKTRACE_LOGGING +test_litest_selftest_LDADD = $(TEST_LIBS) +test_litest_selftest_LDFLAGS = -no-install + # build-test only test_build_pedantic_c99_SOURCES = build-pedantic.c test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror @@ -125,7 +137,7 @@ VALGRIND_FLAGS=--leak-check=full \ --suppressions=$(srcdir)/valgrind.suppressions valgrind: - $(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no + $(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no USING_VALGRIND=yes check: valgrind diff --git a/test/litest-selftest.c b/test/litest-selftest.c new file mode 100644 index 00000000..5ed20ffe --- /dev/null +++ b/test/litest-selftest.c @@ -0,0 +1,228 @@ +#include + +#include +#include + +#include "litest.h" + +START_TEST(litest_assert_trigger) +{ + litest_assert(1 == 2); +} +END_TEST + +START_TEST(litest_assert_notrigger) +{ + litest_assert(1 == 1); +} +END_TEST + +START_TEST(litest_assert_msg_trigger) +{ + litest_assert_msg(1 == 2, "1 is not 2\n"); +} +END_TEST + +START_TEST(litest_assert_msg_NULL_trigger) +{ + litest_assert_msg(1 == 2, NULL); +} +END_TEST + +START_TEST(litest_assert_msg_notrigger) +{ + litest_assert_msg(1 == 1, "1 is not 2\n"); + litest_assert_msg(1 == 1, NULL); +} +END_TEST + +START_TEST(litest_abort_msg_trigger) +{ + litest_abort_msg("message\n"); +} +END_TEST + +START_TEST(litest_abort_msg_NULL_trigger) +{ + litest_abort_msg(NULL); +} +END_TEST + +START_TEST(litest_int_eq_trigger) +{ + int a = 10; + int b = 20; + litest_assert_int_eq(a, b); +} +END_TEST + +START_TEST(litest_int_eq_notrigger) +{ + int a = 10; + int b = 10; + litest_assert_int_eq(a, b); +} +END_TEST + +START_TEST(litest_int_ne_trigger) +{ + int a = 10; + int b = 10; + litest_assert_int_ne(a, b); +} +END_TEST + +START_TEST(litest_int_ne_notrigger) +{ + int a = 10; + int b = 20; + litest_assert_int_ne(a, b); +} +END_TEST + +START_TEST(litest_int_lt_trigger_eq) +{ + int a = 10; + int b = 10; + litest_assert_int_lt(a, b); +} +END_TEST + +START_TEST(litest_int_lt_trigger_gt) +{ + int a = 11; + int b = 10; + litest_assert_int_lt(a, b); +} +END_TEST + +START_TEST(litest_int_lt_notrigger) +{ + int a = 10; + int b = 11; + litest_assert_int_lt(a, b); +} +END_TEST + +START_TEST(litest_int_le_trigger) +{ + int a = 11; + int b = 10; + litest_assert_int_le(a, b); +} +END_TEST + +START_TEST(litest_int_le_notrigger) +{ + int a = 10; + int b = 11; + int c = 10; + litest_assert_int_le(a, b); + litest_assert_int_le(a, c); +} +END_TEST + +START_TEST(litest_int_gt_trigger_eq) +{ + int a = 10; + int b = 10; + litest_assert_int_gt(a, b); +} +END_TEST + +START_TEST(litest_int_gt_trigger_lt) +{ + int a = 9; + int b = 10; + litest_assert_int_gt(a, b); +} +END_TEST + +START_TEST(litest_int_gt_notrigger) +{ + int a = 10; + int b = 9; + litest_assert_int_gt(a, b); +} +END_TEST + +START_TEST(litest_int_ge_trigger) +{ + int a = 9; + int b = 10; + litest_assert_int_ge(a, b); +} +END_TEST + +START_TEST(litest_int_ge_notrigger) +{ + int a = 10; + int b = 9; + int c = 10; + litest_assert_int_ge(a, b); + litest_assert_int_ge(a, c); +} +END_TEST + +static Suite * +litest_assert_macros_suite(void) +{ + TCase *tc; + Suite *s; + + s = suite_create("litest:assert macros"); + tc = tcase_create("assert"); + tcase_add_test_raise_signal(tc, litest_assert_trigger, SIGABRT); + tcase_add_test(tc, litest_assert_notrigger); + tcase_add_test_raise_signal(tc, litest_assert_msg_trigger, SIGABRT); + tcase_add_test_raise_signal(tc, litest_assert_msg_NULL_trigger, SIGABRT); + tcase_add_test(tc, litest_assert_msg_notrigger); + suite_add_tcase(s, tc); + + tc = tcase_create("abort"); + tcase_add_test_raise_signal(tc, litest_abort_msg_trigger, SIGABRT); + tcase_add_test_raise_signal(tc, litest_abort_msg_NULL_trigger, SIGABRT); + suite_add_tcase(s, tc); + + tc = tcase_create("int comparison "); + tcase_add_test_raise_signal(tc, litest_int_eq_trigger, SIGABRT); + tcase_add_test(tc, litest_int_eq_notrigger); + tcase_add_test_raise_signal(tc, litest_int_ne_trigger, SIGABRT); + tcase_add_test(tc, litest_int_ne_notrigger); + tcase_add_test_raise_signal(tc, litest_int_le_trigger, SIGABRT); + tcase_add_test(tc, litest_int_le_notrigger); + tcase_add_test_raise_signal(tc, litest_int_lt_trigger_gt, SIGABRT); + tcase_add_test_raise_signal(tc, litest_int_lt_trigger_eq, SIGABRT); + tcase_add_test(tc, litest_int_lt_notrigger); + tcase_add_test_raise_signal(tc, litest_int_ge_trigger, SIGABRT); + tcase_add_test(tc, litest_int_ge_notrigger); + tcase_add_test_raise_signal(tc, litest_int_gt_trigger_eq, SIGABRT); + tcase_add_test_raise_signal(tc, litest_int_gt_trigger_lt, SIGABRT); + tcase_add_test(tc, litest_int_gt_notrigger); + suite_add_tcase(s, tc); + + return s; +} + +int +main (int argc, char **argv) +{ + int nfailed; + Suite *s; + SRunner *sr; + + /* when running under valgrind we're using nofork mode, so a signal + * raised by a test will fail in valgrind. There's nothing to + * memcheck here anyway, so just skip the valgrind test */ + if (getenv("USING_VALGRIND")) + return EXIT_SUCCESS; + + s = litest_assert_macros_suite(); + sr = srunner_create(s); + + srunner_run_all(sr, CK_ENV); + nfailed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/litest.c b/test/litest.c index aae2663e..e91e499f 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1,5 +1,6 @@ /* * Copyright © 2013 Red Hat, Inc. + * Copyright © 2013 Marcin Slusarz * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -51,6 +52,142 @@ static int in_debugger = -1; static int verbose = 0; +#ifdef HAVE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#include + +/* defined for the litest selftest */ +#ifndef LITEST_DISABLE_BACKTRACE_LOGGING +#define litest_log(...) fprintf(stderr, __VA_ARGS__) +#define litest_vlog(format_, args_) vfprintf(stderr, format_, args_) +#else +#define litest_log(...) /* __VA_ARGS__ */ +#define litest_vlog(...) /* __VA_ARGS__ */ +#endif + +static void +litest_backtrace(void) +{ + unw_cursor_t cursor; + unw_context_t context; + unw_word_t off; + unw_proc_info_t pip; + int ret; + char procname[256]; + Dl_info dlinfo; + /* filename and i are unused ifdef LITEST_SHUTUP */ + const char *filename __attribute__((unused)); + int i __attribute__((unused)) = 0; + + pip.unwind_info = NULL; + ret = unw_getcontext(&context); + if (ret) { + litest_log("unw_getcontext failed: %s [%d]\n", + unw_strerror(ret), + ret); + return; + } + + ret = unw_init_local(&cursor, &context); + if (ret) { + litest_log("unw_init_local failed: %s [%d]\n", + unw_strerror(ret), + ret); + return; + } + + litest_log("\nBacktrace:\n"); + ret = unw_step(&cursor); + while (ret > 0) { + ret = unw_get_proc_info(&cursor, &pip); + if (ret) { + litest_log("unw_get_proc_info failed: %s [%d]\n", + unw_strerror(ret), + ret); + break; + } + + ret = unw_get_proc_name(&cursor, procname, 256, &off); + if (ret && ret != -UNW_ENOMEM) { + if (ret != -UNW_EUNSPEC) + litest_log("unw_get_proc_name failed: %s [%d]\n", + unw_strerror(ret), + ret); + procname[0] = '?'; + procname[1] = 0; + } + + if (dladdr((void *)(pip.start_ip + off), &dlinfo) && + dlinfo.dli_fname && + *dlinfo.dli_fname) + filename = dlinfo.dli_fname; + else + filename = "?"; + + litest_log("%u: %s (%s%s+%#x) [%p]\n", + i++, + filename, + procname, + ret == -UNW_ENOMEM ? "..." : "", + (int)off, + (void *)(pip.start_ip + off)); + + ret = unw_step(&cursor); + if (ret < 0) + litest_log("unw_step failed: %s [%d]\n", + unw_strerror(ret), + ret); + } + litest_log("\n"); +} +#else /* HAVE_LIBUNWIND */ +static inline void +litest_backtrace(void) +{ + /* thou shall install libunwind */ +} +#endif + +void +litest_fail_condition(const char *file, + int line, + const char *func, + const char *condition, + const char *message, + ...) +{ + litest_log("FAILED: %s\n", condition); + + if (message) { + va_list args; + va_start(args, message); + litest_vlog(message, args); + va_end(args); + } + + litest_log("in %s() (%s:%d)\n", func, file, line); + litest_backtrace(); + abort(); +} + +void +litest_fail_comparison_int(const char *file, + int line, + const char *func, + const char *operator, + int a, + int b, + const char *astr, + const char *bstr) +{ + litest_log("FAILED COMPARISON: %s %s %s\n", astr, operator, bstr); + litest_log("Resolved to: %d %s %d\n", a, operator, b); + litest_log("in %s() (%s:%d)\n", func, file, line); + litest_backtrace(); + abort(); +} + struct test { struct list node; char *name; @@ -370,7 +507,7 @@ litest_add_ranged_for_device(const char *name, dev++; } - ck_abort_msg("Invalid test device type"); + litest_abort_msg("Invalid test device type"); } static int @@ -533,13 +670,13 @@ merge_absinfo(const struct input_absinfo *orig, return NULL; abs = calloc(sz, sizeof(*abs)); - ck_assert(abs != NULL); + litest_assert(abs != NULL); nelem = 0; while (orig[nelem].value != -1) { abs[nelem] = orig[nelem]; nelem++; - ck_assert_int_lt(nelem, sz); + litest_assert_int_lt(nelem, sz); } /* just append, if the same axis is present twice, libevdev will @@ -547,10 +684,10 @@ merge_absinfo(const struct input_absinfo *orig, i = 0; while (override && override[i].value != -1) { abs[nelem++] = override[i++]; - ck_assert_int_lt(nelem, sz); + litest_assert_int_lt(nelem, sz); } - ck_assert_int_lt(nelem, sz); + litest_assert_int_lt(nelem, sz); abs[nelem].value = -1; return abs; @@ -567,13 +704,13 @@ merge_events(const int *orig, const int *override) return NULL; events = calloc(sz, sizeof(int)); - ck_assert(events != NULL); + litest_assert(events != NULL); nelem = 0; while (orig[nelem] != -1) { events[nelem] = orig[nelem]; nelem++; - ck_assert_int_lt(nelem, sz); + litest_assert_int_lt(nelem, sz); } /* just append, if the same axis is present twice, libevdev will @@ -581,10 +718,10 @@ merge_events(const int *orig, const int *override) i = 0; while (override && override[i] != -1) { events[nelem++] = override[i++]; - ck_assert_int_le(nelem, sz); + litest_assert_int_le(nelem, sz); } - ck_assert_int_lt(nelem, sz); + litest_assert_int_lt(nelem, sz); events[nelem] = -1; return events; @@ -610,14 +747,14 @@ litest_init_udev_rules(struct litest_test_device *dev) UDEV_RULES_D, UDEV_RULE_PREFIX, dev->shortname); - ck_assert_int_eq(rc, - (int)( - strlen(UDEV_RULES_D) + - strlen(UDEV_RULE_PREFIX) + - strlen(dev->shortname) + 7)); + litest_assert_int_eq(rc, + (int)( + strlen(UDEV_RULES_D) + + strlen(UDEV_RULE_PREFIX) + + strlen(dev->shortname) + 7)); f = fopen(path, "w"); - ck_assert_notnull(f); - ck_assert_int_ge(fputs(dev->udev_rule, f), 0); + litest_assert_notnull(f); + litest_assert_int_ge(fputs(dev->udev_rule, f), 0); fclose(f); litest_reload_udev_rules(); @@ -651,7 +788,7 @@ litest_create(enum litest_device_type which, ck_abort_msg("Invalid device type %d\n", which); d = zalloc(sizeof(*d)); - ck_assert(d != NULL); + litest_assert(d != NULL); udev_file = litest_init_udev_rules(*dev); @@ -661,8 +798,7 @@ litest_create(enum litest_device_type which, if (abs_override || events_override) { if (udev_file) unlink(udev_file); - ck_abort_msg("Custom create cannot" - "be overridden"); + litest_abort_msg("Custom create cannot be overridden"); } return d; @@ -691,7 +827,7 @@ litest_create_context(void) { struct libinput *libinput = libinput_path_create_context(&interface, NULL); - ck_assert_notnull(libinput); + litest_assert_notnull(libinput); libinput_log_set_handler(libinput, litest_log_handler); if (verbose) @@ -732,16 +868,16 @@ litest_add_device_with_overrides(struct libinput *libinput, events_override); path = libevdev_uinput_get_devnode(d->uinput); - ck_assert(path != NULL); + litest_assert(path != NULL); fd = open(path, O_RDWR|O_NONBLOCK); - ck_assert_int_ne(fd, -1); + litest_assert_int_ne(fd, -1); rc = libevdev_new_from_fd(fd, &d->evdev); - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); d->libinput = libinput; d->libinput_device = libinput_path_add_device(d->libinput, path); - ck_assert(d->libinput_device != NULL); + litest_assert(d->libinput_device != NULL); libinput_device_ref(d->libinput_device); if (d->interface) { @@ -836,7 +972,7 @@ litest_event(struct litest_device *d, unsigned int type, return; ret = libevdev_uinput_write_event(d->uinput, type, code, value); - ck_assert_int_eq(ret, 0); + litest_assert_int_eq(ret, 0); } int @@ -1169,9 +1305,9 @@ int litest_scale(const struct litest_device *d, unsigned int axis, double val) { int min, max; - ck_assert_int_ge((int)val, 0); - ck_assert_int_le((int)val, 100); - ck_assert_int_le(axis, (unsigned int)ABS_Y); + litest_assert_int_ge((int)val, 0); + litest_assert_int_le((int)val, 100); + litest_assert_int_le(axis, (unsigned int)ABS_Y); min = d->interface->min[axis]; max = d->interface->max[axis]; @@ -1352,7 +1488,7 @@ litest_assert_empty_queue(struct libinput *li) libinput_dispatch(li); } - ck_assert(empty_queue); + litest_assert(empty_queue); } struct libevdev_uinput * @@ -1378,7 +1514,7 @@ litest_create_uinput_device_from_description(const char *name, const char *devnode; dev = libevdev_new(); - ck_assert(dev != NULL); + litest_assert(dev != NULL); snprintf(buf, sizeof(buf), "litest %s", name); libevdev_set_name(dev, buf); @@ -1393,7 +1529,7 @@ litest_create_uinput_device_from_description(const char *name, while (abs && abs->value != -1) { rc = libevdev_enable_event_code(dev, EV_ABS, abs->value, abs); - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); abs++; } @@ -1406,7 +1542,7 @@ litest_create_uinput_device_from_description(const char *name, rc = libevdev_enable_event_code(dev, type, code, type == EV_ABS ? &default_abs : NULL); } - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); } rc = libevdev_uinput_create_from_device(dev, @@ -1416,16 +1552,16 @@ litest_create_uinput_device_from_description(const char *name, http://cgit.freedesktop.org/libevdev/commit/?id=debe9b030c8069cdf78307888ef3b65830b25122 */ if (rc == -EBADF) rc = -EACCES; - ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + litest_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); libevdev_free(dev); devnode = libevdev_uinput_get_devnode(uinput); - ck_assert_notnull(devnode); + litest_assert_notnull(devnode); fd = open(devnode, O_RDONLY); - ck_assert_int_gt(fd, -1); + litest_assert_int_gt(fd, -1); rc = libevdev_new_from_fd(fd, &dev); - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); /* uinput does not yet support setting the resolution, so we set it * afterwards. This is of course racy as hell but the way we @@ -1437,7 +1573,7 @@ litest_create_uinput_device_from_description(const char *name, rc = libevdev_kernel_set_abs_info(dev, abs->value, abs); - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); } abs++; } @@ -1461,7 +1597,7 @@ litest_create_uinput_abs_device_v(const char *name, (code = va_arg(args, int)) != -1) { *event++ = type; *event++ = code; - ck_assert(event < &events[ARRAY_LENGTH(events) - 2]); + litest_assert(event < &events[ARRAY_LENGTH(events) - 2]); } *event++ = -1; @@ -1508,13 +1644,13 @@ litest_is_button_event(struct libinput_event *event, struct libinput_event_pointer *ptrev; enum libinput_event_type type = LIBINPUT_EVENT_POINTER_BUTTON; - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert(event != NULL); + litest_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); - ck_assert_int_eq(libinput_event_pointer_get_button(ptrev), - button); - ck_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), - state); + litest_assert_int_eq(libinput_event_pointer_get_button(ptrev), + button); + litest_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), + state); return ptrev; } @@ -1527,14 +1663,14 @@ litest_is_axis_event(struct libinput_event *event, struct libinput_event_pointer *ptrev; enum libinput_event_type type = LIBINPUT_EVENT_POINTER_AXIS; - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert(event != NULL); + litest_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); - ck_assert(libinput_event_pointer_has_axis(ptrev, axis)); + litest_assert(libinput_event_pointer_has_axis(ptrev, axis)); if (source != 0) - ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev), - source); + litest_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev), + source); return ptrev; } @@ -1546,8 +1682,8 @@ litest_is_motion_event(struct libinput_event *event) enum libinput_event_type type = LIBINPUT_EVENT_POINTER_MOTION; double x, y, ux, uy; - ck_assert(event != NULL); - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert(event != NULL); + litest_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); x = libinput_event_pointer_get_dx(ptrev); @@ -1556,8 +1692,8 @@ litest_is_motion_event(struct libinput_event *event) uy = libinput_event_pointer_get_dy_unaccelerated(ptrev); /* No 0 delta motion events */ - ck_assert(x != 0.0 || y != 0.0 || - ux != 0.0 || uy != 0.0); + litest_assert(x != 0.0 || y != 0.0 || + ux != 0.0 || uy != 0.0); return ptrev; } @@ -1582,7 +1718,7 @@ litest_is_touch_event(struct libinput_event *event, { struct libinput_event_touch *touch; - ck_assert(event != NULL); + litest_assert(event != NULL); if (type == 0) type = libinput_event_get_type(event); @@ -1592,7 +1728,7 @@ litest_is_touch_event(struct libinput_event *event, case LIBINPUT_EVENT_TOUCH_UP: case LIBINPUT_EVENT_TOUCH_MOTION: case LIBINPUT_EVENT_TOUCH_FRAME: - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert_int_eq(libinput_event_get_type(event), type); break; default: ck_abort_msg("%s: invalid touch type %d\n", __func__, type); @@ -1611,15 +1747,15 @@ litest_is_keyboard_event(struct libinput_event *event, struct libinput_event_keyboard *kevent; enum libinput_event_type type = LIBINPUT_EVENT_KEYBOARD_KEY; - ck_assert_notnull(event); - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert(event != NULL); + litest_assert_int_eq(libinput_event_get_type(event), type); kevent = libinput_event_get_keyboard_event(event); - ck_assert_notnull(kevent); + litest_assert(kevent != NULL); - ck_assert_int_eq(libinput_event_keyboard_get_key(kevent), key); - ck_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), - state); + litest_assert_int_eq(libinput_event_keyboard_get_key(kevent), key); + litest_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), + state); return kevent; } @@ -1634,7 +1770,7 @@ litest_assert_scroll(struct libinput *li, event = libinput_get_event(li); next_event = libinput_get_event(li); - ck_assert(next_event != NULL); /* At least 1 scroll + stop scroll */ + litest_assert(next_event != NULL); /* At least 1 scroll + stop scroll */ while (event) { ptrev = litest_is_axis_event(event, axis, 0); @@ -1644,13 +1780,13 @@ litest_assert_scroll(struct libinput *li, axis); /* Normal scroll event, check dir */ if (minimum_movement > 0) { - ck_assert_int_ge(value, minimum_movement); + litest_assert_int_ge(value, minimum_movement); } else { - ck_assert_int_le(value, minimum_movement); + litest_assert_int_le(value, minimum_movement); } } else { /* Last scroll event, must be 0 */ - ck_assert_int_eq( + litest_assert_int_eq( libinput_event_pointer_get_axis_value(ptrev, axis), 0); } @@ -1670,11 +1806,11 @@ litest_assert_only_typed_events(struct libinput *li, libinput_dispatch(li); event = libinput_get_event(li); - ck_assert_notnull(event); + litest_assert_notnull(event); while (event) { - ck_assert_int_eq(libinput_event_get_type(event), - type); + litest_assert_int_eq(libinput_event_get_type(event), + type); libinput_event_destroy(event); libinput_dispatch(li); event = libinput_get_event(li); diff --git a/test/litest.h b/test/litest.h index 38d3c1b6..80ced698 100644 --- a/test/litest.h +++ b/test/litest.h @@ -32,6 +32,62 @@ #include #include #include +#include + +#define litest_assert(cond) \ + do { \ + if (!(cond)) \ + litest_fail_condition(__FILE__, __LINE__, __func__, \ + #cond, NULL); \ + } while(0) + +#define litest_assert_msg(cond, ...) \ + do { \ + if (!(cond)) \ + litest_fail_condition(__FILE__, __LINE__, __func__, \ + #cond, __VA_ARGS__); \ + } while(0) + +#define litest_abort_msg(...) \ + litest_fail_condition(__FILE__, __LINE__, __func__, \ + "aborting", __VA_ARGS__); \ + +#define litest_assert_notnull(cond) \ + do { \ + if ((cond) == NULL) \ + litest_fail_condition(__FILE__, __LINE__, __func__, \ + #cond, " expected to be not NULL"); \ + } while(0) + +#define litest_assert_comparison_int_(a_, op_, b_) \ + do { \ + __typeof__(a_) _a = a_; \ + __typeof__(b_) _b = b_; \ + if (trunc(_a) != _a || trunc(_b) != _b) \ + litest_abort_msg("litest_assert_int_* used for non-integer value\n"); \ + if (!((_a) op_ (_b))) \ + litest_fail_comparison_int(__FILE__, __LINE__, __func__,\ + #op_, _a, _b, \ + #a_, #b_); \ + } while(0) + +#define litest_assert_int_eq(a_, b_) \ + litest_assert_comparison_int_(a_, ==, b_) + +#define litest_assert_int_ne(a_, b_) \ + litest_assert_comparison_int_(a_, !=, b_) + +#define litest_assert_int_lt(a_, b_) \ + litest_assert_comparison_int_(a_, <, b_) + +#define litest_assert_int_le(a_, b_) \ + litest_assert_comparison_int_(a_, <=, b_) + +#define litest_assert_int_ge(a_, b_) \ + litest_assert_comparison_int_(a_, >=, b_) + +#define litest_assert_int_gt(a_, b_) \ + litest_assert_comparison_int_(a_, >, b_) enum litest_device_type { LITEST_NO_DEVICE = -1, @@ -109,6 +165,23 @@ struct libinput *litest_create_context(void); void litest_disable_log_handler(struct libinput *libinput); void litest_restore_log_handler(struct libinput *libinput); +void +litest_fail_condition(const char *file, + int line, + const char *func, + const char *condition, + const char *message, + ...); +void +litest_fail_comparison_int(const char *file, + int line, + const char *func, + const char *operator, + int a, + int b, + const char *astr, + const char *bstr); + void litest_add(const char *name, void *func, enum litest_device_feature required_feature, enum litest_device_feature excluded_feature); diff --git a/test/valgrind.suppressions b/test/valgrind.suppressions index 5aef8a41..50b5c58a 100644 --- a/test/valgrind.suppressions +++ b/test/valgrind.suppressions @@ -13,3 +13,11 @@ ... fun:mtdev_put_event } +{ + libunwind:msync_uninitialized_bytes + Memcheck:Param + msync(start) + fun:__msync_nocancel + ... + fun:litest_backtrace +} From dc3dbc4dfc88248ce0573b2ea39978e816ab352a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 8 May 2015 08:16:39 +1000 Subject: [PATCH 140/165] test: add pointer comparison macros Signed-off-by: Peter Hutterer --- test/litest-selftest.c | 141 +++++++++++++++++++++++++++++++++++++++++ test/litest.c | 12 ++++ test/litest.h | 26 ++++++++ 3 files changed, 179 insertions(+) diff --git a/test/litest-selftest.c b/test/litest-selftest.c index 5ed20ffe..f7974770 100644 --- a/test/litest-selftest.c +++ b/test/litest-selftest.c @@ -164,6 +164,131 @@ START_TEST(litest_int_ge_notrigger) } END_TEST +START_TEST(litest_ptr_eq_notrigger) +{ + int v = 10; + int *a = &v; + int *b = &v; + int c = NULL; + int d = NULL; + + litest_assert_ptr_eq(a, b); + litest_assert_ptr_eq(c, d); +} +END_TEST + +START_TEST(litest_ptr_eq_trigger) +{ + int v = 10; + int v2 = 11; + int *a = &v; + int *b = &v2; + + litest_assert_ptr_eq(a, b); +} +END_TEST + +START_TEST(litest_ptr_eq_trigger_NULL) +{ + int v = 10; + int *a = &v; + int *b = NULL; + + litest_assert_ptr_eq(a, b); +} +END_TEST + +START_TEST(litest_ptr_eq_trigger_NULL2) +{ + int v = 10; + int *a = &v; + int *b = NULL; + + litest_assert_ptr_eq(b, a); +} +END_TEST + +START_TEST(litest_ptr_ne_trigger) +{ + int v = 10; + int *a = &v; + int *b = &v; + + litest_assert_ptr_ne(a, b); +} +END_TEST + +START_TEST(litest_ptr_ne_trigger_NULL) +{ + int *a = NULL; + + litest_assert_ptr_ne(a, NULL); +} +END_TEST + +START_TEST(litest_ptr_ne_trigger_NULL2) +{ + int *a = NULL; + + litest_assert_ptr_ne(NULL, a); +} +END_TEST + +START_TEST(litest_ptr_ne_notrigger) +{ + int v1 = 10; + int v2 = 10; + int *a = &v1; + int *b = &v2; + int *c = NULL; + + litest_assert_ptr_ne(a, b); + litest_assert_ptr_ne(a, c); + litest_assert_ptr_ne(c, b); +} +END_TEST + +START_TEST(litest_ptr_null_notrigger) +{ + int *a = NULL; + + litest_assert_ptr_null(a); + litest_assert_ptr_null(NULL); +} +END_TEST + +START_TEST(litest_ptr_null_trigger) +{ + int v; + int *a = &v; + + litest_assert_ptr_null(a); +} +END_TEST + +START_TEST(litest_ptr_notnull_notrigger) +{ + int v; + int *a = &v; + + litest_assert_ptr_notnull(a); +} +END_TEST + +START_TEST(litest_ptr_notnull_trigger) +{ + int *a = NULL; + + litest_assert_ptr_notnull(a); +} +END_TEST + +START_TEST(litest_ptr_notnull_trigger_NULL) +{ + litest_assert_ptr_notnull(NULL); +} +END_TEST + static Suite * litest_assert_macros_suite(void) { @@ -201,6 +326,22 @@ litest_assert_macros_suite(void) tcase_add_test(tc, litest_int_gt_notrigger); suite_add_tcase(s, tc); + tc = tcase_create("pointer comparison "); + tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger, SIGABRT); + tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL, SIGABRT); + tcase_add_test_raise_signal(tc, litest_ptr_eq_trigger_NULL2, SIGABRT); + tcase_add_test(tc, litest_ptr_eq_notrigger); + tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger, SIGABRT); + tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL, SIGABRT); + tcase_add_test_raise_signal(tc, litest_ptr_ne_trigger_NULL2, SIGABRT); + tcase_add_test(tc, litest_ptr_ne_notrigger); + tcase_add_test_raise_signal(tc, litest_ptr_null_trigger, SIGABRT); + tcase_add_test(tc, litest_ptr_null_notrigger); + tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger, SIGABRT); + tcase_add_test_raise_signal(tc, litest_ptr_notnull_trigger_NULL, SIGABRT); + tcase_add_test(tc, litest_ptr_notnull_notrigger); + suite_add_tcase(s, tc); + return s; } diff --git a/test/litest.c b/test/litest.c index e91e499f..c0d35af7 100644 --- a/test/litest.c +++ b/test/litest.c @@ -188,6 +188,18 @@ litest_fail_comparison_int(const char *file, abort(); } +void +litest_fail_comparison_ptr(const char *file, + int line, + const char *func, + const char *comparison) +{ + litest_log("FAILED COMPARISON: %s\n", comparison); + litest_log("in %s() (%s:%d)\n", func, file, line); + litest_backtrace(); + abort(); +} + struct test { struct list node; char *name; diff --git a/test/litest.h b/test/litest.h index 80ced698..40b11bd8 100644 --- a/test/litest.h +++ b/test/litest.h @@ -89,6 +89,27 @@ #define litest_assert_int_gt(a_, b_) \ litest_assert_comparison_int_(a_, >, b_) +#define litest_assert_comparison_ptr_(a_, op_, b_) \ + do { \ + __typeof__(a_) _a = a_; \ + __typeof__(b_) _b = b_; \ + if (!((_a) op_ (_b))) \ + litest_fail_comparison_ptr(__FILE__, __LINE__, __func__,\ + #a_ " " #op_ " " #b_); \ + } while(0) + +#define litest_assert_ptr_eq(a_, b_) \ + litest_assert_comparison_ptr_(a_, ==, b_) + +#define litest_assert_ptr_ne(a_, b_) \ + litest_assert_comparison_ptr_(a_, !=, b_) + +#define litest_assert_ptr_null(a_) \ + litest_assert_comparison_ptr_(a_, ==, NULL) + +#define litest_assert_ptr_notnull(a_) \ + litest_assert_comparison_ptr_(a_, !=, NULL) + enum litest_device_type { LITEST_NO_DEVICE = -1, LITEST_SYNAPTICS_CLICKPAD = -2, @@ -181,6 +202,11 @@ litest_fail_comparison_int(const char *file, int b, const char *astr, const char *bstr); +void +litest_fail_comparison_ptr(const char *file, + int line, + const char *func, + const char *comparison); void litest_add(const char *name, void *func, enum litest_device_feature required_feature, From b8af89f173b5ddb2a074f6c28774043f4e77e210 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 8 May 2015 09:31:44 +1000 Subject: [PATCH 141/165] test: extract file:line from backtrace with addr2line libunwind gives us a file and an address and usually a function name. Beyond that, it's mostly guessing. Fork off addr2line to resolve the addresses that libunwind gives us, if we succeed we get a backtrace like this: Backtrace: 0: litest_fail_comparison_int() (./test/litest.c:268) 1: disable_button_scrolling() (./test/pointer.c:115) 2: middlebutton_doubleclick() (./test/pointer.c:991) 3: /lib64/libcheck.so.0 (srunner_run+0x7f5) [0x7f6c12d8c025] 4: litest_run() (./test/litest.c:689) 5: main() (./test/pointer.c:1280) 6: /lib64/libc.so.6 (__libc_start_main+0xf0) [0x7f6c11a73790] 7: ./test/test-pointer (_start+0x29) [0x403d99] 8: ? (?+0x29) [0x29] Note: I intentionally swapped function/file name in the output to make it easier to spot which one is fully resolved and which one is the basic libunwind output. Signed-off-by: Peter Hutterer --- configure.ac | 5 +++ test/litest.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 75951fda..f4c2b7d9 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,11 @@ if test "x$HAVE_LIBUNWIND" = "xyes"; then AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support]) fi AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes]) +AC_PATH_PROG(ADDR2LINE, [addr2line]) +if test "x$ADDR2LINE" != "x"; then + AC_DEFINE_UNQUOTED(HAVE_ADDR2LINE, 1, [addr2line found]) + AC_DEFINE_UNQUOTED(ADDR2LINE, ["$ADDR2LINE"], [Path to addr2line]) +fi AC_CHECK_LIB([m], [atan2]) AC_CHECK_LIB([rt], [clock_gettime]) diff --git a/test/litest.c b/test/litest.c index c0d35af7..8df5e920 100644 --- a/test/litest.c +++ b/test/litest.c @@ -66,6 +66,68 @@ static int verbose = 0; #define litest_vlog(...) /* __VA_ARGS__ */ #endif +static char cwd[PATH_MAX]; + +static bool +litest_backtrace_get_lineno(const char *executable, + unw_word_t addr, + char *file_return, + int *line_return) +{ +#if HAVE_ADDR2LINE + FILE* f; + char buffer[PATH_MAX]; + char *s; + int i; + + if (!cwd[0]) + getcwd(cwd, sizeof(cwd)); + + sprintf (buffer, + ADDR2LINE " -C -e %s -i %lx", + executable, + (unsigned long) addr); + + f = popen(buffer, "r"); + if (f == NULL) { + litest_log("Failed to execute: %s\n", buffer); + return false; + } + + buffer[0] = '?'; + fgets(buffer, sizeof(buffer), f); + fclose(f); + + if (buffer[0] == '?') + return false; + + s = strrchr(buffer, ':'); + if (!s) + return false; + + *s = '\0'; + s++; + sscanf(s, "%d", line_return); + + /* now strip cwd from buffer */ + s = buffer; + i = 0; + while(cwd[i] == *s) { + *s = '\0'; + s++; + i++; + } + + if (i > 0) + *(--s) = '.'; + strcpy(file_return, s); + + return true; +#else /* HAVE_ADDR2LINE */ + return false; +#endif +} + static void litest_backtrace(void) { @@ -100,6 +162,10 @@ litest_backtrace(void) litest_log("\nBacktrace:\n"); ret = unw_step(&cursor); while (ret > 0) { + char file[PATH_MAX]; + int line; + bool have_lineno = false; + ret = unw_get_proc_info(&cursor, &pip); if (ret) { litest_log("unw_get_proc_info failed: %s [%d]\n", @@ -120,19 +186,33 @@ litest_backtrace(void) if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname && - *dlinfo.dli_fname) + *dlinfo.dli_fname) { filename = dlinfo.dli_fname; - else + have_lineno = litest_backtrace_get_lineno(filename, + (pip.start_ip + off), + file, + &line); + } else { filename = "?"; + } - litest_log("%u: %s (%s%s+%#x) [%p]\n", - i++, - filename, - procname, - ret == -UNW_ENOMEM ? "..." : "", - (int)off, - (void *)(pip.start_ip + off)); + if (have_lineno) { + litest_log("%u: %s() (%s:%d)\n", + i, + procname, + file, + line); + } else { + litest_log("%u: %s (%s%s+%#x) [%p]\n", + i, + filename, + procname, + ret == -UNW_ENOMEM ? "..." : "", + (int)off, + (void *)(pip.start_ip + off)); + } + i++; ret = unw_step(&cursor); if (ret < 0) litest_log("unw_step failed: %s [%d]\n", From 13c7b47d878e2e54a16414b3163f98215b7a493d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 4 May 2015 11:40:39 +1000 Subject: [PATCH 142/165] test: convert all helper functions to use the litest macros Signed-off-by: Peter Hutterer --- test/device.c | 2 +- test/log.c | 4 ++-- test/misc.c | 4 ++-- test/pointer.c | 28 ++++++++++++++-------------- test/touchpad.c | 9 +++++---- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/test/device.c b/test/device.c index b580e004..ca8c4a40 100644 --- a/test/device.c +++ b/test/device.c @@ -827,7 +827,7 @@ assert_device_ignored(struct libinput *li, struct input_absinfo *absinfo) -1); device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); - ck_assert(device == NULL); + litest_assert_ptr_null(device); libevdev_uinput_destroy(uinput); } diff --git a/test/log.c b/test/log.c index 139c0020..3dbfa90a 100644 --- a/test/log.c +++ b/test/log.c @@ -58,8 +58,8 @@ simple_log_handler(struct libinput *libinput, { log_handler_called++; if (log_handler_context) - ck_assert(libinput == log_handler_context); - ck_assert(format != NULL); + litest_assert_ptr_eq(libinput, log_handler_context); + litest_assert_notnull(format); } START_TEST(log_default_priority) diff --git a/test/misc.c b/test/misc.c index e9d54622..9eca0548 100644 --- a/test/misc.c +++ b/test/misc.c @@ -64,7 +64,7 @@ create_simple_test_device(const char *name, ...) }; evdev = libevdev_new(); - ck_assert(evdev != NULL); + litest_assert_notnull(evdev); libevdev_set_name(evdev, name); va_start(args, name); @@ -82,7 +82,7 @@ create_simple_test_device(const char *name, ...) rc = libevdev_uinput_create_from_device(evdev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uinput); - ck_assert_int_eq(rc, 0); + litest_assert_int_eq(rc, 0); libevdev_free(evdev); return uinput; diff --git a/test/pointer.c b/test/pointer.c index a47b95c1..eb63c729 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -53,7 +53,7 @@ get_accelerated_motion_event(struct libinput *li) return ptrev; } - ck_abort_msg("No accelerated pointer motion event found"); + litest_abort_msg("No accelerated pointer motion event found"); return NULL; } @@ -90,11 +90,11 @@ test_relative_event(struct litest_device *dev, int dx, int dy) actual_dir = atan2(ev_dx, ev_dy); /* Check the length of the motion vector (tolerate 1.0 indifference). */ - ck_assert(fabs(expected_length) >= actual_length); + litest_assert(fabs(expected_length) >= actual_length); /* Check the direction of the motion vector (tolerate 2π/4 radians * indifference). */ - ck_assert(fabs(expected_dir - actual_dir) < M_PI_2); + litest_assert(fabs(expected_dir - actual_dir) < M_PI_2); libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev)); @@ -112,7 +112,7 @@ disable_button_scrolling(struct litest_device *device) LIBINPUT_CONFIG_SCROLL_NO_SCROLL); expected = LIBINPUT_CONFIG_STATUS_SUCCESS; - ck_assert_int_eq(status, expected); + litest_assert_int_eq(status, expected); } START_TEST(pointer_motion_relative) @@ -146,16 +146,16 @@ test_absolute_event(struct litest_device *dev, double x, double y) libinput_dispatch(li); event = libinput_get_event(li); - ck_assert_notnull(event); - ck_assert_int_eq(libinput_event_get_type(event), type); + litest_assert_notnull(event); + litest_assert_int_eq(libinput_event_get_type(event), type); ptrev = libinput_event_get_pointer_event(event); - ck_assert(ptrev != NULL); + litest_assert(ptrev != NULL); ex = libinput_event_pointer_get_absolute_x_transformed(ptrev, 100); ey = libinput_event_pointer_get_absolute_y_transformed(ptrev, 100); - ck_assert_int_eq((int)(ex + 0.5), (int)x); - ck_assert_int_eq((int)(ey + 0.5), (int)y); + litest_assert_int_eq((int)(ex + 0.5), (int)x); + litest_assert_int_eq((int)(ey + 0.5), (int)y); libinput_event_destroy(event); } @@ -192,8 +192,8 @@ test_unaccel_event(struct litest_device *dev, int dx, int dy) ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev); ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev); - ck_assert_int_eq(dx, ev_dx); - ck_assert_int_eq(dy, ev_dy); + litest_assert_int_eq(dx, ev_dx); + litest_assert_int_eq(dy, ev_dy); libinput_event_destroy(event); @@ -383,10 +383,10 @@ test_wheel_event(struct litest_device *dev, int which, int amount) axis, LIBINPUT_POINTER_AXIS_SOURCE_WHEEL); - ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis), + litest_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis), expected); - ck_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis), - discrete); + litest_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis), + discrete); libinput_event_destroy(event); } diff --git a/test/touchpad.c b/test/touchpad.c index e3052d48..3f4db53d 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -2936,13 +2936,14 @@ END_TEST static void enable_edge_scroll(struct litest_device *dev) { - enum libinput_config_status status; + enum libinput_config_status status, expected; struct libinput_device *device = dev->libinput_device; status = libinput_device_config_scroll_set_method(device, LIBINPUT_CONFIG_SCROLL_EDGE); - ck_assert_int_eq(status, - LIBINPUT_CONFIG_STATUS_SUCCESS); + + expected = LIBINPUT_CONFIG_STATUS_SUCCESS; + litest_assert_int_eq(status, expected); } START_TEST(touchpad_edge_scroll) @@ -4265,7 +4266,7 @@ assert_btnevent_from_device(struct litest_device *device, e = libinput_get_event(li); litest_is_button_event(e, button, state); - ck_assert_ptr_eq(libinput_event_get_device(e), device->libinput_device); + litest_assert_ptr_eq(libinput_event_get_device(e), device->libinput_device); libinput_event_destroy(e); } From 75fd354aa3909eceef0856ad7cee02b1fab20ccc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 18 May 2015 15:59:09 +1000 Subject: [PATCH 143/165] test: drop unneeded includes Signed-off-by: Peter Hutterer --- test/log.c | 1 - test/path.c | 1 - 2 files changed, 2 deletions(-) diff --git a/test/log.c b/test/log.c index 139c0020..4eb554dd 100644 --- a/test/log.c +++ b/test/log.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include "litest.h" diff --git a/test/path.c b/test/path.c index 64c5d7f4..df649da6 100644 --- a/test/path.c +++ b/test/path.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include "litest.h" From b6eb43b537d36f9651ff943ae8d4fab5595ebcf9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 10:24:36 +1000 Subject: [PATCH 144/165] path: be more lenient waiting for udev to settle things Running tests in parallel can exceed the current 100ms max timeout for udev to settle things. Since this pretty much only triggers in test cases anyway, let's be more lenient here. Signed-off-by: Peter Hutterer --- src/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path.c b/src/path.c index 832a1fd8..f9b1a6c2 100644 --- a/src/path.c +++ b/src/path.c @@ -310,7 +310,7 @@ udev_device_from_devnode(struct libinput *libinput, dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev); count++; - if (count > 10) { + if (count > 50) { log_bug_libinput(libinput, "udev device never initialized (%s)\n", devnode); From d05363316c6bae4531082f38b7e74275aa7c7f8a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 10:33:24 +1000 Subject: [PATCH 145/165] test: parse the LITEST_VERBOSE environment variable --verbose only works when running a specific test, sometimes we need make check to be more verbose. Set this by default for make check, the log becomes a lot more expressive that way Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 12 ++++++++++++ test/Makefile.am | 2 ++ test/litest.c | 3 +++ 3 files changed, 17 insertions(+) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index 6e993e08..d2680d9f 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -63,4 +63,16 @@ $ CK_RUN_SUITE="device:wheel" CK_RUN_CASE="wheel only" ./test/test-device Check and litest currently do not provide a way to run a specific test function only. + +@section test-verbosity Controlling test output + +Each test supports the `--verbose` commandline option to enable debugging +output, see libinput_log_set_priority() for details. The `LITEST_VERBOSE` +environment variable, if set, also enables verbose mode. + +@code +$ ./test/test-device --verbose +$ LITEST_VERBOSE=1 make check +@endcode + */ diff --git a/test/Makefile.am b/test/Makefile.am index 0df89b2c..46959fdf 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -118,6 +118,8 @@ test_build_linker_LDADD = $(top_builddir)/src/libinput.la $(top_builddir)/src/li test_build_cxx_SOURCES = build-cxx.cc test_build_cxx_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter $(AM_CXXFLAGS) +AM_TESTS_ENVIRONMENT= LITEST_VERBOSE=1; export LITEST_VERBOSE; + if HAVE_VALGRIND VALGRIND_FLAGS=--leak-check=full \ --quiet \ diff --git a/test/litest.c b/test/litest.c index 2aac5a34..9cc87740 100644 --- a/test/litest.c +++ b/test/litest.c @@ -500,6 +500,9 @@ litest_run(int argc, char **argv) { } } + if (getenv("LITEST_VERBOSE")) + verbose = 1; + srunner_run_all(sr, CK_ENV); failed = srunner_ntests_failed(sr); srunner_free(sr); From 120a5b3384f47566d394c3868cee2d1402935241 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 10:57:27 +1000 Subject: [PATCH 146/165] test: add a note to one of the device tests If we get two different sysnames for the device, this test doesn't test anything much, so it's better to fail here. But add a comment so that when it fails it's quite obvious why. Signed-off-by: Peter Hutterer --- test/device.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/device.c b/test/device.c index b580e004..22569d26 100644 --- a/test/device.c +++ b/test/device.c @@ -313,6 +313,9 @@ START_TEST(device_reenable_syspath_changed) litest_device = litest_add_device(li, LITEST_MOUSE); device2 = litest_device->libinput_device; + /* Note: if the sysname isn't the same, some other device got added + * or removed while this test was running. This is unlikely and + * would result in a false positive, so let's fail the test here */ ck_assert_str_eq(libinput_device_get_sysname(device1), libinput_device_get_sysname(device2)); From cd163b3b8a2146ed40eb1ae023dd91c48288bbcc Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 16:01:55 +1000 Subject: [PATCH 147/165] Allow disabling tapping on a device without tapping The doc says this "always succeeds", not "segfaults". Signed-off-by: Peter Hutterer --- src/libinput.c | 7 ++++--- test/touchpad.c | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libinput.c b/src/libinput.c index 5ef7edfb..cefc5ec1 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1708,11 +1708,12 @@ libinput_device_config_tap_set_enabled(struct libinput_device *device, enable != LIBINPUT_CONFIG_TAP_DISABLED) return LIBINPUT_CONFIG_STATUS_INVALID; - if (enable && - libinput_device_config_tap_get_finger_count(device) == 0) - return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + if (libinput_device_config_tap_get_finger_count(device) == 0) + return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED : + LIBINPUT_CONFIG_STATUS_SUCCESS; return device->config.tap->set_enabled(device, enable); + } LIBINPUT_EXPORT enum libinput_config_tap_state diff --git a/test/touchpad.c b/test/touchpad.c index e3052d48..f7f9dd5a 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -3154,6 +3154,9 @@ START_TEST(touchpad_tap_is_not_available) ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, LIBINPUT_CONFIG_TAP_ENABLED), LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + ck_assert_int_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED), + LIBINPUT_CONFIG_STATUS_SUCCESS); } END_TEST From f70db17d218c7049134c17a6cc8871d5e4c9e379 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 16:22:14 +1000 Subject: [PATCH 148/165] Allow disabling of middle button emulation where it doesn't exist Signed-off-by: Peter Hutterer --- src/libinput.c | 11 ++++++++--- test/pointer.c | 28 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/libinput.c b/src/libinput.c index cefc5ec1..444e9ce9 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1988,17 +1988,22 @@ libinput_device_config_middle_emulation_set_enabled( struct libinput_device *device, enum libinput_config_middle_emulation_state enable) { + int available = + libinput_device_config_middle_emulation_is_available(device); + switch (enable) { case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + if (!available) + return LIBINPUT_CONFIG_STATUS_SUCCESS; + break; case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + if (!available) + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; break; default: return LIBINPUT_CONFIG_STATUS_INVALID; } - if (!libinput_device_config_middle_emulation_is_available(device)) - return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; - return device->config.middle_emulation->set(device, enable); } diff --git a/test/pointer.c b/test/pointer.c index 43c21ba3..cbc38023 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -1208,7 +1208,7 @@ START_TEST(middlebutton_default_clickpad) status = libinput_device_config_middle_emulation_set_enabled(device, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); - ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); status = libinput_device_config_middle_emulation_set_enabled(device, 3); ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID); @@ -1237,6 +1237,30 @@ START_TEST(middlebutton_default_touchpad) } END_TEST +START_TEST(middlebutton_default_disabled) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *device = dev->libinput_device; + enum libinput_config_middle_emulation_state state; + enum libinput_config_status status; + int available; + + available = libinput_device_config_middle_emulation_is_available(device); + ck_assert(!available); + state = libinput_device_config_middle_emulation_get_enabled(device); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + state = libinput_device_config_middle_emulation_get_default_enabled( + device); + ck_assert_int_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + status = libinput_device_config_middle_emulation_set_enabled(device, + LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED); +} +END_TEST + int main (int argc, char **argv) { litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); @@ -1274,5 +1298,7 @@ int main (int argc, char **argv) litest_add("pointer:middlebutton", middlebutton_default_enabled, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_POINTINGSTICK); litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY); litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); + litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON); + return litest_run(argc, argv); } From 42fa56c9ee5ed7b10c2094ec3af8d8935195564d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 10:57:11 +1000 Subject: [PATCH 149/165] doc: clarify a comment what a "no device" test case means Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index d2680d9f..5bcaee05 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -49,7 +49,8 @@ device:group: In the above example, the "device:wheel" suite is run for the "wheel only" and the "blackwidow" device. Both devices are automatically instantiated through uinput by litest. The "no device" entry signals that litest does not -instantiate a uinput device for a specific test. +instantiate a uinput device for a specific test (though the test itself may +instantiate one). Check provides two filters through environment variables: CK_RUN_SUITE and CK_RUN_CASE. They may be used independently or combined to narrow From 015151de3a92cffddfd4db110cd6630b2f83f54d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 09:21:26 +1000 Subject: [PATCH 150/165] test: move opening { to next line Signed-off-by: Peter Hutterer --- test/litest.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/litest.c b/test/litest.c index 9cc87740..f637d4ef 100644 --- a/test/litest.c +++ b/test/litest.c @@ -67,11 +67,13 @@ struct suite { static struct litest_device *current_device; -struct litest_device *litest_current_device(void) { +struct litest_device *litest_current_device(void) +{ return current_device; } -void litest_set_current_device(struct litest_device *device) { +void litest_set_current_device(struct litest_device *device) +{ current_device = device; } @@ -461,7 +463,8 @@ static const struct option opts[] = { }; int -litest_run(int argc, char **argv) { +litest_run(int argc, char **argv) +{ struct suite *s, *snext; int failed; SRunner *sr = NULL; From e39dca063bb27546c52970ba4a83d28d05563893 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Mon, 18 May 2015 15:13:23 +0200 Subject: [PATCH 151/165] Rename MODEL_SYSTEM76_CLEVO to MODEL_CLEVO_W740SU The System76 Galago Ultra Pro is a rebranded Clevo W740SU with changed firmware strings. To my knowledge, the Clevo W740SU and all its rebrands possess smooth touchpads. In the original bug report[1], a Galago Ultra Pro was returned to the original DMI strings by flashing another firmware. This resulted in the model identified as MODEL_SYSTEM76_CLEVO. Since the actual manufacturer of the W740SU is Clevo and the CLEVO hwdb entry already properly identifies other W740SU rebrands like the Schenker S413, the model should be renamed to match. [1]: https://bugs.freedesktop.org/show_bug.cgi?id=90170#c3 https://bugs.freedesktop.org/show_bug.cgi?id=90353 Signed-off-by: Jan Alexander Steffens (heftig) Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-buttons.c | 2 +- src/evdev.c | 2 +- src/evdev.h | 2 +- udev/90-libinput-model-quirks.hwdb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c index d6acbf0a..43e983b2 100644 --- a/src/evdev-mt-touchpad-buttons.c +++ b/src/evdev-mt-touchpad-buttons.c @@ -656,9 +656,9 @@ tp_click_get_default_method(struct tp_dispatch *tp) switch (device->model) { case EVDEV_MODEL_CHROMEBOOK: case EVDEV_MODEL_SYSTEM76_BONOBO: - case EVDEV_MODEL_SYSTEM76_CLEVO: case EVDEV_MODEL_SYSTEM76_GALAGO: case EVDEV_MODEL_SYSTEM76_KUDU: + case EVDEV_MODEL_CLEVO_W740SU: return LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; default: break; diff --git a/src/evdev.c b/src/evdev.c index 4ce92501..5f1a943c 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1449,9 +1449,9 @@ evdev_read_model(struct evdev_device *device) { "LIBINPUT_MODEL_LENOVO_X230", EVDEV_MODEL_LENOVO_X230 }, { "LIBINPUT_MODEL_CHROMEBOOK", EVDEV_MODEL_CHROMEBOOK }, { "LIBINPUT_MODEL_SYSTEM76_BONOBO", EVDEV_MODEL_SYSTEM76_BONOBO }, - { "LIBINPUT_MODEL_SYSTEM76_CLEVO", EVDEV_MODEL_SYSTEM76_CLEVO }, { "LIBINPUT_MODEL_SYSTEM76_GALAGO", EVDEV_MODEL_SYSTEM76_GALAGO }, { "LIBINPUT_MODEL_SYSTEM76_KUDU", EVDEV_MODEL_SYSTEM76_KUDU }, + { "LIBINPUT_MODEL_CLEVO_W740SU", EVDEV_MODEL_CLEVO_W740SU }, { NULL, EVDEV_MODEL_DEFAULT }, }; const struct model_map *m = model_map; diff --git a/src/evdev.h b/src/evdev.h index 20c0b556..4281a159 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -98,9 +98,9 @@ enum evdev_device_model { EVDEV_MODEL_LENOVO_X230, EVDEV_MODEL_CHROMEBOOK, EVDEV_MODEL_SYSTEM76_BONOBO, - EVDEV_MODEL_SYSTEM76_CLEVO, EVDEV_MODEL_SYSTEM76_GALAGO, EVDEV_MODEL_SYSTEM76_KUDU, + EVDEV_MODEL_CLEVO_W740SU, }; struct mt_slot { diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 0cdf2b2b..048e5cc6 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -55,7 +55,7 @@ libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrbonp5* # Clevo libinput:name:SynPS/2 Synaptics TouchPad:dmi:*pnW740SU*rnW740SU* - LIBINPUT_MODEL_SYSTEM76_CLEVO=1 + LIBINPUT_MODEL_CLEVO_W740SU=1 # Galago Ultra Pro libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnSystem76*pvrgalu1* From b84c60737fad1f81b0e3dbf5d63b41295a2ecaf1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 May 2015 16:40:24 +1000 Subject: [PATCH 152/165] test: add missing linebreak in debug error message Signed-off-by: Peter Hutterer --- test/litest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/litest.h b/test/litest.h index 40b11bd8..15c03afe 100644 --- a/test/litest.h +++ b/test/litest.h @@ -56,7 +56,7 @@ do { \ if ((cond) == NULL) \ litest_fail_condition(__FILE__, __LINE__, __func__, \ - #cond, " expected to be not NULL"); \ + #cond, " expected to be not NULL\n"); \ } while(0) #define litest_assert_comparison_int_(a_, op_, b_) \ From db567c01a4137983c8e267e8dfe54a36f4588c24 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 14:44:16 +1000 Subject: [PATCH 153/165] evdev: log a bug if releasing a key fails, but don't loop forever If a physical button is down but releasing said button doesn't actually release it, we loop endlessly. Detect that and log a bug instead. Reproducible: trigger a tap-n-drag on a touchpad device, then remove it. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 5f1a943c..90c4e2c0 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2330,6 +2330,9 @@ release_pressed_keys(struct evdev_device *device) for (code = 0; code < KEY_CNT; code++) { int count = get_key_down_count(device, code); + if (count == 0) + continue; + if (count > 1) { log_bug_libinput(libinput, "Key %d is down %d times.\n", @@ -2337,25 +2340,31 @@ release_pressed_keys(struct evdev_device *device) count); } - while (get_key_down_count(device, code) > 0) { - switch (get_key_type(code)) { - case EVDEV_KEY_TYPE_NONE: - break; - case EVDEV_KEY_TYPE_KEY: - evdev_keyboard_notify_key( - device, - time, - code, - LIBINPUT_KEY_STATE_RELEASED); - break; - case EVDEV_KEY_TYPE_BUTTON: - evdev_pointer_notify_physical_button( - device, - time, - evdev_to_left_handed(device, code), - LIBINPUT_BUTTON_STATE_RELEASED); - break; - } + switch (get_key_type(code)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + evdev_keyboard_notify_key( + device, + time, + code, + LIBINPUT_KEY_STATE_RELEASED); + break; + case EVDEV_KEY_TYPE_BUTTON: + evdev_pointer_notify_physical_button( + device, + time, + evdev_to_left_handed(device, code), + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + + count = get_key_down_count(device, code); + if (count != 0) { + log_bug_libinput(libinput, + "Releasing key %d failed.\n", + code); + break; } } } From 15a67e4fac1c072fc2722fea29dfdfb5556de2a5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 15:52:17 +1000 Subject: [PATCH 154/165] touchpad: rename all interface functions for clarity Add "interface" to the name to make things a bit clearer. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 3ebb5591..c85986ee 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -739,10 +739,10 @@ tp_handle_state(struct tp_dispatch *tp, } static void -tp_process(struct evdev_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, - uint64_t time) +tp_interface_process(struct evdev_dispatch *dispatch, + struct evdev_device *device, + struct input_event *e, + uint64_t time) { struct tp_dispatch *tp = (struct tp_dispatch *)dispatch; @@ -779,7 +779,7 @@ tp_remove_sendevents(struct tp_dispatch *tp) } static void -tp_remove(struct evdev_dispatch *dispatch) +tp_interface_remove(struct evdev_dispatch *dispatch) { struct tp_dispatch *tp = (struct tp_dispatch*)dispatch; @@ -792,7 +792,7 @@ tp_remove(struct evdev_dispatch *dispatch) } static void -tp_destroy(struct evdev_dispatch *dispatch) +tp_interface_destroy(struct evdev_dispatch *dispatch) { struct tp_dispatch *tp = (struct tp_dispatch*)dispatch; @@ -953,8 +953,8 @@ tp_keyboard_event(uint64_t time, struct libinput_event *event, void *data) } static void -tp_device_added(struct evdev_device *device, - struct evdev_device *added_device) +tp_interface_device_added(struct evdev_device *device, + struct evdev_device *added_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; unsigned int bus_tp = libevdev_get_id_bustype(device->evdev), @@ -997,8 +997,8 @@ tp_device_added(struct evdev_device *device, } static void -tp_device_removed(struct evdev_device *device, - struct evdev_device *removed_device) +tp_interface_device_removed(struct evdev_device *device, + struct evdev_device *removed_device) { struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch; struct libinput_device *dev; @@ -1036,8 +1036,8 @@ tp_device_removed(struct evdev_device *device, } static void -tp_tag_device(struct evdev_device *device, - struct udev_device *udev_device) +tp_interface_tag_device(struct evdev_device *device, + struct udev_device *udev_device) { int bustype; @@ -1059,14 +1059,14 @@ tp_tag_device(struct evdev_device *device, } static struct evdev_dispatch_interface tp_interface = { - tp_process, - tp_remove, - tp_destroy, - tp_device_added, - tp_device_removed, - tp_device_removed, /* device_suspended, treat as remove */ - tp_device_added, /* device_resumed, treat as add */ - tp_tag_device, + tp_interface_process, + tp_interface_remove, + tp_interface_destroy, + tp_interface_device_added, + tp_interface_device_removed, + tp_interface_device_removed, /* device_suspended, treat as remove */ + tp_interface_device_added, /* device_resumed, treat as add */ + tp_interface_tag_device, }; static void @@ -1495,7 +1495,7 @@ evdev_mt_touchpad_create(struct evdev_device *device) tp->model = tp_get_model(device); if (tp_init(tp, device) != 0) { - tp_destroy(&tp->base); + tp_interface_destroy(&tp->base); return NULL; } From 58174416cac8977886a792fd0afd823d11776206 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 15:37:18 +1000 Subject: [PATCH 155/165] evdev: move release_pressed_keys() up Just a move to make the next diff easier Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev.c | 104 ++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 90c4e2c0..e0097545 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -760,6 +760,58 @@ fallback_process(struct evdev_dispatch *dispatch, } } +static void +release_pressed_keys(struct evdev_device *device) +{ + struct libinput *libinput = device->base.seat->libinput; + uint64_t time; + int code; + + if ((time = libinput_now(libinput)) == 0) + return; + + for (code = 0; code < KEY_CNT; code++) { + int count = get_key_down_count(device, code); + + if (count == 0) + continue; + + if (count > 1) { + log_bug_libinput(libinput, + "Key %d is down %d times.\n", + code, + count); + } + + switch (get_key_type(code)) { + case EVDEV_KEY_TYPE_NONE: + break; + case EVDEV_KEY_TYPE_KEY: + evdev_keyboard_notify_key( + device, + time, + code, + LIBINPUT_KEY_STATE_RELEASED); + break; + case EVDEV_KEY_TYPE_BUTTON: + evdev_pointer_notify_physical_button( + device, + time, + evdev_to_left_handed(device, code), + LIBINPUT_BUTTON_STATE_RELEASED); + break; + } + + count = get_key_down_count(device, code); + if (count != 0) { + log_bug_libinput(libinput, + "Releasing key %d failed.\n", + code); + break; + } + } +} + static void fallback_destroy(struct evdev_dispatch *dispatch) { @@ -2317,58 +2369,6 @@ evdev_stop_scroll(struct evdev_device *device, device->scroll.direction = 0; } -static void -release_pressed_keys(struct evdev_device *device) -{ - struct libinput *libinput = device->base.seat->libinput; - uint64_t time; - int code; - - if ((time = libinput_now(libinput)) == 0) - return; - - for (code = 0; code < KEY_CNT; code++) { - int count = get_key_down_count(device, code); - - if (count == 0) - continue; - - if (count > 1) { - log_bug_libinput(libinput, - "Key %d is down %d times.\n", - code, - count); - } - - switch (get_key_type(code)) { - case EVDEV_KEY_TYPE_NONE: - break; - case EVDEV_KEY_TYPE_KEY: - evdev_keyboard_notify_key( - device, - time, - code, - LIBINPUT_KEY_STATE_RELEASED); - break; - case EVDEV_KEY_TYPE_BUTTON: - evdev_pointer_notify_physical_button( - device, - time, - evdev_to_left_handed(device, code), - LIBINPUT_BUTTON_STATE_RELEASED); - break; - } - - count = get_key_down_count(device, code); - if (count != 0) { - log_bug_libinput(libinput, - "Releasing key %d failed.\n", - code); - break; - } - } -} - void evdev_notify_suspended_device(struct evdev_device *device) { From 736e292fc4a24fa56bf60655e914ff87049b12e3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 May 2015 15:57:11 +1000 Subject: [PATCH 156/165] evdev: add an interface hook to suspend a device The touchpad carries enough state around that calling release_all_keys() isn't enough to properly suspend it. e.g. a button down after tapping won't be released by trying to release the physical button for it. We need to clear the state properly, but that's interface-specific so add a new hook for it. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 10 ++++++++++ src/evdev.c | 14 ++++++++++++-- src/evdev.h | 4 ++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index c85986ee..026f9ee2 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -853,6 +853,15 @@ tp_suspend(struct tp_dispatch *tp, struct evdev_device *device) } } +static void +tp_interface_suspend(struct evdev_dispatch *dispatch, + struct evdev_device *device) +{ + struct tp_dispatch *tp = (struct tp_dispatch *)dispatch; + + tp_clear_state(tp); +} + static void tp_resume(struct tp_dispatch *tp, struct evdev_device *device) { @@ -1060,6 +1069,7 @@ tp_interface_tag_device(struct evdev_device *device, static struct evdev_dispatch_interface tp_interface = { tp_interface_process, + tp_interface_suspend, tp_interface_remove, tp_interface_destroy, tp_interface_device_added, diff --git a/src/evdev.c b/src/evdev.c index e0097545..24368470 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -812,6 +812,13 @@ release_pressed_keys(struct evdev_device *device) } } +static void +fallback_suspend(struct evdev_dispatch *dispatch, + struct evdev_device *device) +{ + release_pressed_keys(device); +} + static void fallback_destroy(struct evdev_dispatch *dispatch) { @@ -869,6 +876,7 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device, struct evdev_dispatch_interface fallback_interface = { fallback_process, + fallback_suspend, NULL, /* remove */ fallback_destroy, NULL, /* device_added */ @@ -2414,14 +2422,16 @@ evdev_device_suspend(struct evdev_device *device) { evdev_notify_suspended_device(device); + if (device->dispatch->interface->suspend) + device->dispatch->interface->suspend(device->dispatch, + device); + if (device->source) { libinput_remove_source(device->base.seat->libinput, device->source); device->source = NULL; } - release_pressed_keys(device); - if (device->mtdev) { mtdev_close_delete(device->mtdev); device->mtdev = NULL; diff --git a/src/evdev.h b/src/evdev.h index 4281a159..22e6b019 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -227,6 +227,10 @@ struct evdev_dispatch_interface { struct input_event *event, uint64_t time); + /* Device is being suspended */ + void (*suspend)(struct evdev_dispatch *dispatch, + struct evdev_device *device); + /* Device is being removed (may be NULL) */ void (*remove)(struct evdev_dispatch *dispatch); From 4889a0c3c2d609ec7a337a68b2dd1be1389a7043 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 8 May 2015 13:06:25 +1000 Subject: [PATCH 157/165] evdev: sync the initial x/y position for touchscreens Unlikely, but there's the odd chance of the first touch coming in with the same X or Y coordinate the kernel already has internally. This would generate a x/0 or 0/y event in libinput. Sync the slot state on init, at least for Protocol B devices. For Protocol A devices and mtdev, don't bother. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev.c | 12 +++++++-- test/touch.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 24368470..9a0d3d7b 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1781,8 +1781,16 @@ evdev_configure_mt_device(struct evdev_device *device) for (slot = 0; slot < num_slots; ++slot) { slots[slot].seat_slot = -1; - slots[slot].point.x = 0; - slots[slot].point.y = 0; + + if (evdev_need_mtdev(device)) + continue; + + slots[slot].point.x = libevdev_get_slot_value(evdev, + slot, + ABS_MT_POSITION_X); + slots[slot].point.y = libevdev_get_slot_value(evdev, + slot, + ABS_MT_POSITION_Y); } device->mt.slots = slots; device->mt.slots_len = num_slots; diff --git a/test/touch.c b/test/touch.c index d78deedd..828c6edf 100644 --- a/test/touch.c +++ b/test/touch.c @@ -582,9 +582,79 @@ START_TEST(touch_protocol_a_2fg_touch) } END_TEST +START_TEST(touch_initial_state) +{ + struct litest_device *dev; + struct libinput *libinput1, *libinput2; + struct libinput_event *ev1, *ev2; + struct libinput_event_touch *t1, *t2; + struct libinput_device *device1, *device2; + int axis = _i; /* looped test */ + + dev = litest_current_device(); + device1 = dev->libinput_device; + libinput_device_config_tap_set_enabled(device1, + LIBINPUT_CONFIG_TAP_DISABLED); + + libinput1 = dev->libinput; + litest_touch_down(dev, 0, 40, 60); + litest_touch_up(dev, 0); + + /* device is now on some x/y value */ + litest_drain_events(libinput1); + + libinput2 = litest_create_context(); + device2 = libinput_path_add_device(libinput2, + libevdev_uinput_get_devnode( + dev->uinput)); + libinput_device_config_tap_set_enabled(device2, + LIBINPUT_CONFIG_TAP_DISABLED); + litest_drain_events(libinput2); + + if (axis == ABS_X) + litest_touch_down(dev, 0, 40, 70); + else + litest_touch_down(dev, 0, 70, 60); + litest_touch_up(dev, 0); + + litest_wait_for_event(libinput1); + litest_wait_for_event(libinput2); + + while (libinput_next_event_type(libinput1)) { + ev1 = libinput_get_event(libinput1); + ev2 = libinput_get_event(libinput2); + + t1 = litest_is_touch_event(ev1, 0); + t2 = litest_is_touch_event(ev2, 0); + + ck_assert_int_eq(libinput_event_get_type(ev1), + libinput_event_get_type(ev2)); + + if (libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_UP || + libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_FRAME) + break; + + ck_assert_int_eq(libinput_event_touch_get_x(t1), + libinput_event_touch_get_x(t2)); + ck_assert_int_eq(libinput_event_touch_get_y(t1), + libinput_event_touch_get_y(t2)); + + libinput_event_destroy(ev1); + libinput_event_destroy(ev2); + } + + libinput_event_destroy(ev1); + libinput_event_destroy(ev2); + + libinput_unref(libinput2); +} +END_TEST + int main(int argc, char **argv) { + struct range axes = { ABS_X, ABS_Y + 1}; + litest_add("touch:frame", touch_frame_events, LITEST_TOUCH, LITEST_ANY); litest_add_no_device("touch:abs-transform", touch_abs_transform); litest_add_no_device("touch:many-slots", touch_many_slots); @@ -605,5 +675,7 @@ main(int argc, char **argv) litest_add("touch:protocol a", touch_protocol_a_touch, LITEST_PROTOCOL_A, LITEST_ANY); litest_add("touch:protocol a", touch_protocol_a_2fg_touch, LITEST_PROTOCOL_A, LITEST_ANY); + litest_add_ranged("touch:state", touch_initial_state, LITEST_TOUCH, LITEST_PROTOCOL_A, &axes); + return litest_run(argc, argv); } From 180564c780ab6906df06d6402bf0dc646f171610 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 8 May 2015 14:41:42 +1000 Subject: [PATCH 158/165] evdev: sync the initial state for absolute pointer devices Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev.c | 2 ++ test/pointer.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index 9a0d3d7b..1a71e4c0 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1865,6 +1865,8 @@ evdev_configure_device(struct evdev_device *device) device->abs.fake_resolution = 1; device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_X); device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_Y); + device->abs.point.x = device->abs.absinfo_x->value; + device->abs.point.y = device->abs.absinfo_y->value; if (evdev_is_fake_mt_device(device)) { udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN; diff --git a/test/pointer.c b/test/pointer.c index 56acd2ef..ba88eabb 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -172,6 +172,61 @@ START_TEST(pointer_motion_absolute) } END_TEST +START_TEST(pointer_absolute_initial_state) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *libinput1, *libinput2; + struct libinput_event *ev1, *ev2; + struct libinput_event_pointer *p1, *p2; + int axis = _i; /* looped test */ + + dev = litest_current_device(); + libinput1 = dev->libinput; + litest_touch_down(dev, 0, 40, 60); + litest_touch_up(dev, 0); + + /* device is now on some x/y value */ + litest_drain_events(libinput1); + + libinput2 = litest_create_context(); + libinput_path_add_device(libinput2, + libevdev_uinput_get_devnode(dev->uinput)); + litest_drain_events(libinput2); + + if (axis == ABS_X) + litest_touch_down(dev, 0, 40, 70); + else + litest_touch_down(dev, 0, 70, 60); + litest_touch_up(dev, 0); + + litest_wait_for_event(libinput1); + litest_wait_for_event(libinput2); + + while (libinput_next_event_type(libinput1)) { + ev1 = libinput_get_event(libinput1); + ev2 = libinput_get_event(libinput2); + + ck_assert_int_eq(libinput_event_get_type(ev1), + LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE); + ck_assert_int_eq(libinput_event_get_type(ev1), + libinput_event_get_type(ev2)); + + p1 = libinput_event_get_pointer_event(ev1); + p2 = libinput_event_get_pointer_event(ev2); + + ck_assert_int_eq(libinput_event_pointer_get_absolute_x(p1), + libinput_event_pointer_get_absolute_x(p2)); + ck_assert_int_eq(libinput_event_pointer_get_absolute_y(p1), + libinput_event_pointer_get_absolute_y(p2)); + + libinput_event_destroy(ev1); + libinput_event_destroy(ev2); + } + + libinput_unref(libinput2); +} +END_TEST + static void test_unaccel_event(struct litest_device *dev, int dx, int dy) { @@ -1265,6 +1320,8 @@ END_TEST int main (int argc, char **argv) { + struct range axis_range = {ABS_X, ABS_Y + 1}; + litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY); @@ -1302,5 +1359,7 @@ int main (int argc, char **argv) litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON); + litest_add_ranged("pointer:state", pointer_absolute_initial_state, LITEST_ABSOLUTE, LITEST_ANY, &axis_range); + return litest_run(argc, argv); } From af4ff07f6b0543a87bb20914d6bf4775d6360bdb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 8 May 2015 13:30:13 +1000 Subject: [PATCH 159/165] touchpad: sync the initial touch state Unlikely, but there's the odd chance of the first touch coming in with the same X or Y coordinate the kernel already has internally. This would generate a bogus delta on the second event when the touch coordinate jumps from 0/y or x/0 to the real coordinates. For touchpads with distance support this is a real issue since the default value for a touch distance is > 0. Signed-off-by: Peter Hutterer Tested-by: Benjamin Tissoires Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 28 +++++++++++++++++++ test/touchpad.c | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 026f9ee2..409d81e9 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1087,6 +1087,28 @@ tp_init_touch(struct tp_dispatch *tp, t->has_ended = true; } +static void +tp_sync_touch(struct tp_dispatch *tp, + struct evdev_device *device, + struct tp_touch *t, + int slot) +{ + struct libevdev *evdev = device->evdev; + + if (!libevdev_fetch_slot_value(evdev, + slot, + ABS_MT_POSITION_X, + &t->point.x)) + t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X); + if (!libevdev_fetch_slot_value(evdev, + slot, + ABS_MT_POSITION_Y, + &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); +} + static int tp_init_slots(struct tp_dispatch *tp, struct evdev_device *device) @@ -1134,6 +1156,12 @@ tp_init_slots(struct tp_dispatch *tp, for (i = 0; i < tp->ntouches; i++) tp_init_touch(tp, &tp->touches[i]); + /* Always sync the first touch so we get ABS_X/Y synced on + * single-touch touchpads */ + tp_sync_touch(tp, device, &tp->touches[0], 0); + for (i = 1; i < tp->num_slots; i++) + tp_sync_touch(tp, device, &tp->touches[i], i); + return 0; } diff --git a/test/touchpad.c b/test/touchpad.c index 6f8c036d..381349d4 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4531,9 +4531,69 @@ START_TEST(touchpad_trackpoint_no_trackpoint) } END_TEST +START_TEST(touchpad_initial_state) +{ + struct litest_device *dev; + struct libinput *libinput1, *libinput2; + struct libinput_event *ev1, *ev2; + struct libinput_event_pointer *p1, *p2; + int axis = _i; /* looped test */ + int x = 40, y = 60; + + dev = litest_current_device(); + libinput1 = dev->libinput; + + libinput_device_config_tap_set_enabled(dev->libinput_device, + LIBINPUT_CONFIG_TAP_DISABLED); + + litest_touch_down(dev, 0, x, y); + litest_touch_up(dev, 0); + + /* device is now on some x/y value */ + litest_drain_events(libinput1); + + libinput2 = litest_create_context(); + libinput_path_add_device(libinput2, + libevdev_uinput_get_devnode(dev->uinput)); + litest_drain_events(libinput2); + + if (axis == ABS_X) + x = 30; + else + y = 30; + litest_touch_down(dev, 0, x, y); + litest_touch_move_to(dev, 0, x, y, 80, 80, 10, 1); + litest_touch_up(dev, 0); + + litest_wait_for_event(libinput1); + litest_wait_for_event(libinput2); + + while (libinput_next_event_type(libinput1)) { + ev1 = libinput_get_event(libinput1); + ev2 = libinput_get_event(libinput2); + + p1 = litest_is_motion_event(ev1); + p2 = litest_is_motion_event(ev2); + + ck_assert_int_eq(libinput_event_get_type(ev1), + libinput_event_get_type(ev2)); + + ck_assert_int_eq(libinput_event_pointer_get_dx(p1), + libinput_event_pointer_get_dx(p2)); + ck_assert_int_eq(libinput_event_pointer_get_dy(p1), + libinput_event_pointer_get_dy(p2)); + libinput_event_destroy(ev1); + libinput_event_destroy(ev2); + } + + libinput_unref(libinput2); +} +END_TEST + int main(int argc, char **argv) { struct range multitap_range = {3, 8}; + struct range axis_range = {ABS_X, ABS_Y + 1}; litest_add("touchpad:motion", touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY); litest_add("touchpad:motion", touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); @@ -4676,5 +4736,7 @@ int main(int argc, char **argv) litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons_2fg_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); + litest_add_ranged("touchpad:state", touchpad_initial_state, LITEST_TOUCHPAD, LITEST_ANY, &axis_range); + return litest_run(argc, argv); } From 9dbeb9d0f7b54d140d6b3fe6d1d07c7fa5fe20e0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 09:36:01 +1000 Subject: [PATCH 160/165] test: move main() into litest This allows us to filter things based on argv before setting up tests, etc. Signed-off-by: Peter Hutterer --- test/device.c | 5 ++--- test/keyboard.c | 6 ++---- test/litest.c | 10 +++++++++- test/litest.h | 2 +- test/log.c | 5 ++--- test/misc.c | 5 ++--- test/path.c | 6 ++---- test/pointer.c | 5 ++--- test/touch.c | 6 ++---- test/touchpad.c | 5 ++--- test/trackpoint.c | 5 ++--- test/udev.c | 6 ++---- 12 files changed, 30 insertions(+), 36 deletions(-) diff --git a/test/device.c b/test/device.c index 22569d26..c44efabc 100644 --- a/test/device.c +++ b/test/device.c @@ -946,7 +946,8 @@ START_TEST(device_wheel_only) } END_TEST -int main (int argc, char **argv) +void +litest_setup_tests(void) { struct range abs_range = { 0, ABS_MISC }; struct range abs_mt_range = { ABS_MT_SLOT + 1, ABS_CNT }; @@ -987,6 +988,4 @@ int main (int argc, char **argv) litest_add_no_device("device:invalid devices", abs_mt_device_missing_res); litest_add("device:wheel", device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE); - - return litest_run(argc, argv); } diff --git a/test/keyboard.c b/test/keyboard.c index 03d62ddd..1c8092b5 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -310,14 +310,12 @@ START_TEST(keyboard_keys_bad_device) } END_TEST -int -main(int argc, char **argv) +void +litest_setup_tests(void) { litest_add_no_device("keyboard:seat key count", keyboard_seat_key_count); litest_add_no_device("keyboard:key counting", keyboard_ignore_no_pressed_release); litest_add_no_device("keyboard:key counting", keyboard_key_auto_release); litest_add("keyboard:keys", keyboard_has_key, LITEST_KEYS, LITEST_ANY); litest_add("keyboard:keys", keyboard_keys_bad_device, LITEST_ANY, LITEST_ANY); - - return litest_run(argc, argv); } diff --git a/test/litest.c b/test/litest.c index f637d4ef..c0b515b1 100644 --- a/test/litest.c +++ b/test/litest.c @@ -462,7 +462,7 @@ static const struct option opts[] = { { 0, 0, 0, 0} }; -int +static int litest_run(int argc, char **argv) { struct suite *s, *snext; @@ -1886,3 +1886,11 @@ litest_semi_mt_touch_up(struct litest_device *d, litest_event(d, EV_SYN, SYN_REPORT, 0); } + +int +main(int argc, char **argv) +{ + litest_setup_tests(); + + return litest_run(argc, argv); +} diff --git a/test/litest.h b/test/litest.h index 38d3c1b6..b6aaa6f5 100644 --- a/test/litest.h +++ b/test/litest.h @@ -130,7 +130,7 @@ void litest_add_ranged_no_device(const char *name, void *func, const struct range *range); -int litest_run(int argc, char **argv); +extern void litest_setup_tests(void); struct litest_device * litest_create_device(enum litest_device_type which); struct litest_device * litest_add_device(struct libinput *libinput, enum litest_device_type which); diff --git a/test/log.c b/test/log.c index 4eb554dd..504f4c8d 100644 --- a/test/log.c +++ b/test/log.c @@ -139,12 +139,11 @@ START_TEST(log_priority) } END_TEST -int main (int argc, char **argv) +void +litest_setup_tests(void) { litest_add_no_device("log:defaults", log_default_priority); litest_add_no_device("log:logging", log_handler_invoked); litest_add_no_device("log:logging", log_handler_NULL); litest_add_no_device("log:logging", log_priority); - - return litest_run(argc, argv); } diff --git a/test/misc.c b/test/misc.c index e9d54622..ec6d820a 100644 --- a/test/misc.c +++ b/test/misc.c @@ -583,7 +583,8 @@ START_TEST(trackpoint_accel_parser) } END_TEST -int main (int argc, char **argv) +void +litest_setup_tests(void) { litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE); @@ -600,6 +601,4 @@ int main (int argc, char **argv) litest_add_no_device("misc:dpi parser", dpi_parser); litest_add_no_device("misc:wheel click parser", wheel_click_parser); litest_add_no_device("misc:trackpoint accel parser", trackpoint_accel_parser); - - return litest_run(argc, argv); } diff --git a/test/path.c b/test/path.c index df649da6..c28c6ff4 100644 --- a/test/path.c +++ b/test/path.c @@ -874,8 +874,8 @@ START_TEST(path_seat_recycle) } END_TEST -int -main(int argc, char **argv) +void +litest_setup_tests(void) { litest_add_no_device("path:create", path_create_NULL); litest_add_no_device("path:create", path_create_invalid); @@ -896,6 +896,4 @@ main(int argc, char **argv) litest_add_for_device("path:device events", path_remove_device, LITEST_SYNAPTICS_CLICKPAD); litest_add_for_device("path:device events", path_double_remove_device, LITEST_SYNAPTICS_CLICKPAD); litest_add_no_device("path:seat", path_seat_recycle); - - return litest_run(argc, argv); } diff --git a/test/pointer.c b/test/pointer.c index cbc38023..22a4f3c0 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -1261,7 +1261,8 @@ START_TEST(middlebutton_default_disabled) } END_TEST -int main (int argc, char **argv) +void +litest_setup_tests(void) { litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY); litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY); @@ -1299,6 +1300,4 @@ int main (int argc, char **argv) litest_add("pointer:middlebutton", middlebutton_default_clickpad, LITEST_CLICKPAD, LITEST_ANY); litest_add("pointer:middlebutton", middlebutton_default_touchpad, LITEST_TOUCHPAD, LITEST_CLICKPAD); litest_add("pointer:middlebutton", middlebutton_default_disabled, LITEST_ANY, LITEST_BUTTON); - - return litest_run(argc, argv); } diff --git a/test/touch.c b/test/touch.c index d78deedd..1d2d9e5b 100644 --- a/test/touch.c +++ b/test/touch.c @@ -582,8 +582,8 @@ START_TEST(touch_protocol_a_2fg_touch) } END_TEST -int -main(int argc, char **argv) +void +litest_setup_tests(void) { litest_add("touch:frame", touch_frame_events, LITEST_TOUCH, LITEST_ANY); litest_add_no_device("touch:abs-transform", touch_abs_transform); @@ -604,6 +604,4 @@ main(int argc, char **argv) litest_add("touch:protocol a", touch_protocol_a_init, LITEST_PROTOCOL_A, LITEST_ANY); litest_add("touch:protocol a", touch_protocol_a_touch, LITEST_PROTOCOL_A, LITEST_ANY); litest_add("touch:protocol a", touch_protocol_a_2fg_touch, LITEST_PROTOCOL_A, LITEST_ANY); - - return litest_run(argc, argv); } diff --git a/test/touchpad.c b/test/touchpad.c index f7f9dd5a..22273565 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -4530,7 +4530,8 @@ START_TEST(touchpad_trackpoint_no_trackpoint) } END_TEST -int main(int argc, char **argv) +void +litest_setup_tests(void) { struct range multitap_range = {3, 8}; @@ -4674,6 +4675,4 @@ int main(int argc, char **argv) litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons_softbuttons, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_buttons_2fg_scroll, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); litest_add_for_device("touchpad:trackpoint", touchpad_trackpoint_no_trackpoint, LITEST_SYNAPTICS_TRACKPOINT_BUTTONS); - - return litest_run(argc, argv); } diff --git a/test/trackpoint.c b/test/trackpoint.c index ac6173e4..9fcce6f7 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -130,12 +130,11 @@ START_TEST(trackpoint_scroll_source) } END_TEST -int main(int argc, char **argv) +void +litest_setup_tests(void) { litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY); litest_add("trackpoint:middlebutton", trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY); litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY); litest_add("trackpoint:scroll", trackpoint_scroll_source, LITEST_POINTINGSTICK, LITEST_ANY); - - return litest_run(argc, argv); } diff --git a/test/udev.c b/test/udev.c index c351bed5..4ec956bc 100644 --- a/test/udev.c +++ b/test/udev.c @@ -502,8 +502,8 @@ START_TEST(udev_seat_recycle) } END_TEST -int -main(int argc, char **argv) +void +litest_setup_tests(void) { litest_add_no_device("udev:create", udev_create_NULL); litest_add_no_device("udev:create", udev_create_seat0); @@ -518,6 +518,4 @@ main(int argc, char **argv) litest_add_for_device("udev:suspend", udev_suspend_resume, LITEST_SYNAPTICS_CLICKPAD); litest_add_for_device("udev:device events", udev_device_sysname, LITEST_SYNAPTICS_CLICKPAD); litest_add_for_device("udev:seat", udev_seat_recycle, LITEST_SYNAPTICS_CLICKPAD); - - return litest_run(argc, argv); } From b2fd428f969245cd53116c843c6587e06098fce1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 09:46:54 +1000 Subject: [PATCH 161/165] test: move argument parsing into a separate function Signed-off-by: Peter Hutterer --- test/litest.c | 62 +++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/test/litest.c b/test/litest.c index c0b515b1..d5810185 100644 --- a/test/litest.c +++ b/test/litest.c @@ -456,12 +456,6 @@ struct libinput_interface interface = { .close_restricted = close_restricted, }; -static const struct option opts[] = { - { "list", 0, 0, 'l' }, - { "verbose", 0, 0, 'v' }, - { 0, 0, 0, 0} -}; - static int litest_run(int argc, char **argv) { @@ -482,27 +476,6 @@ litest_run(int argc, char **argv) srunner_add_suite(sr, s->suite); } - while(1) { - int c; - int option_index = 0; - - c = getopt_long(argc, argv, "", opts, &option_index); - if (c == -1) - break; - switch(c) { - case 'l': - litest_list_tests(&all_tests); - return 0; - case 'v': - verbose = 1; - break; - default: - fprintf(stderr, "usage: %s [--list]\n", argv[0]); - return 1; - - } - } - if (getenv("LITEST_VERBOSE")) verbose = 1; @@ -1887,9 +1860,44 @@ litest_semi_mt_touch_up(struct litest_device *d, litest_event(d, EV_SYN, SYN_REPORT, 0); } +static int +litest_parse_argv(int argc, char **argv) +{ + static const struct option opts[] = { + { "list", 0, 0, 'l' }, + { "verbose", 0, 0, 'v' }, + { 0, 0, 0, 0} + }; + + while(1) { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, "", opts, &option_index); + if (c == -1) + break; + switch(c) { + case 'l': + litest_list_tests(&all_tests); + exit(0); + case 'v': + verbose = 1; + break; + default: + fprintf(stderr, "usage: %s [--list]\n", argv[0]); + return 1; + } + } + + return 0; +} + int main(int argc, char **argv) { + if (litest_parse_argv(argc, argv) != 0) + return EXIT_FAILURE; + litest_setup_tests(); return litest_run(argc, argv); From f3d37d96538f7d0b42240eeea11471360c392d89 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 10:12:39 +1000 Subject: [PATCH 162/165] test: add filtering to litest framework Complementary to CK_RUN_SUITE and CK_RUN_CASE, this filters on actual test function names with a simple fnmatch. ./test/test-touchpad --filter-test="*1fg_tap*" Most of this patch is renaming litest_add_* to _litest_add_* so we can use the macros to get at the function names. Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 8 +- test/litest.c | 192 ++++++++++++++++++++++++++++----------------- test/litest.h | 55 ++++++++----- 3 files changed, 161 insertions(+), 94 deletions(-) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index 5bcaee05..079018c0 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -62,8 +62,12 @@ $ CK_RUN_CASE="wheel only" ./test/test-device $ CK_RUN_SUITE="device:wheel" CK_RUN_CASE="wheel only" ./test/test-device @endcode -Check and litest currently do not provide a way to run a specific test -function only. +The `--filter-test` argument enables selective running of tests through +basic shell-style function name matching. For example: + +@code +$ ./test/test-touchpad --filter-test="*1fg_tap*" +@endcode @section test-verbosity Controlling test output diff --git a/test/litest.c b/test/litest.c index d5810185..fb0403db 100644 --- a/test/litest.c +++ b/test/litest.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ static int in_debugger = -1; static int verbose = 0; +const char *filter_test = NULL; struct test { struct list node; @@ -187,6 +189,7 @@ litest_drop_udev_rules(void) static void litest_add_tcase_for_device(struct suite *suite, + const char *funcname, void *func, const struct litest_test_device *dev, const struct range *range) @@ -254,53 +257,6 @@ litest_add_tcase_no_device(struct suite *suite, suite_add_tcase(suite->suite, t->tc); } -static void -litest_add_tcase(struct suite *suite, void *func, - enum litest_device_feature required, - enum litest_device_feature excluded, - const struct range *range) -{ - struct litest_test_device **dev = devices; - - assert(required >= LITEST_DISABLE_DEVICE); - assert(excluded >= LITEST_DISABLE_DEVICE); - - if (required == LITEST_DISABLE_DEVICE && - excluded == LITEST_DISABLE_DEVICE) { - litest_add_tcase_no_device(suite, func, range); - } else if (required != LITEST_ANY || excluded != LITEST_ANY) { - while (*dev) { - if (((*dev)->features & required) == required && - ((*dev)->features & excluded) == 0) - litest_add_tcase_for_device(suite, func, *dev, range); - dev++; - } - } else { - while (*dev) { - litest_add_tcase_for_device(suite, func, *dev, range); - dev++; - } - } -} - -void -litest_add_no_device(const char *name, void *func) -{ - litest_add(name, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE); -} - -void -litest_add_ranged_no_device(const char *name, - void *func, - const struct range *range) -{ - litest_add_ranged(name, - func, - LITEST_DISABLE_DEVICE, - LITEST_DISABLE_DEVICE, - range); -} - static struct suite * get_suite(const char *name) { @@ -325,38 +281,113 @@ get_suite(const char *name) return s; } -void -litest_add(const char *name, - void *func, - enum litest_device_feature required, - enum litest_device_feature excluded) +static void +litest_add_tcase(const char *suite_name, + const char *funcname, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded, + const struct range *range) { - litest_add_ranged(name, func, required, excluded, NULL); + struct litest_test_device **dev = devices; + struct suite *suite; + + assert(required >= LITEST_DISABLE_DEVICE); + assert(excluded >= LITEST_DISABLE_DEVICE); + + if (filter_test && + fnmatch(filter_test, funcname, 0) != 0) + return; + + suite = get_suite(suite_name); + + if (required == LITEST_DISABLE_DEVICE && + excluded == LITEST_DISABLE_DEVICE) { + litest_add_tcase_no_device(suite, func, range); + } else if (required != LITEST_ANY || excluded != LITEST_ANY) { + while (*dev) { + if (((*dev)->features & required) == required && + ((*dev)->features & excluded) == 0) + litest_add_tcase_for_device(suite, + funcname, + func, + *dev, + range); + dev++; + } + } else { + while (*dev) { + litest_add_tcase_for_device(suite, + funcname, + func, + *dev, + range); + dev++; + } + } } void -litest_add_ranged(const char *name, - void *func, - enum litest_device_feature required, - enum litest_device_feature excluded, - const struct range *range) +_litest_add_no_device(const char *name, const char *funcname, void *func) { - litest_add_tcase(get_suite(name), func, required, excluded, range); + _litest_add(name, funcname, func, LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE); } void -litest_add_for_device(const char *name, - void *func, - enum litest_device_type type) -{ - litest_add_ranged_for_device(name, func, type, NULL); -} - -void -litest_add_ranged_for_device(const char *name, +_litest_add_ranged_no_device(const char *name, + const char *funcname, void *func, - enum litest_device_type type, const struct range *range) +{ + _litest_add_ranged(name, + funcname, + func, + LITEST_DISABLE_DEVICE, + LITEST_DISABLE_DEVICE, + range); +} + +void +_litest_add(const char *name, + const char *funcname, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded) +{ + _litest_add_ranged(name, + funcname, + func, + required, + excluded, + NULL); +} + +void +_litest_add_ranged(const char *name, + const char *funcname, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded, + const struct range *range) +{ + litest_add_tcase(name, funcname, func, required, excluded, range); +} + +void +_litest_add_for_device(const char *name, + const char *funcname, + void *func, + enum litest_device_type type) +{ + _litest_add_ranged_for_device(name, funcname, func, type, NULL); +} + +void +_litest_add_ranged_for_device(const char *name, + const char *funcname, + void *func, + enum litest_device_type type, + const struct range *range) { struct suite *s; struct litest_test_device **dev = devices; @@ -366,7 +397,11 @@ litest_add_ranged_for_device(const char *name, s = get_suite(name); while (*dev) { if ((*dev)->type == type) { - litest_add_tcase_for_device(s, func, *dev, range); + litest_add_tcase_for_device(s, + funcname, + func, + *dev, + range); return; } dev++; @@ -1863,9 +1898,15 @@ litest_semi_mt_touch_up(struct litest_device *d, static int litest_parse_argv(int argc, char **argv) { + enum { + OPT_FILTER_TEST, + OPT_LIST, + OPT_VERBOSE, + }; static const struct option opts[] = { - { "list", 0, 0, 'l' }, - { "verbose", 0, 0, 'v' }, + { "filter-test", 1, 0, OPT_FILTER_TEST }, + { "list", 0, 0, OPT_LIST }, + { "verbose", 0, 0, OPT_VERBOSE }, { 0, 0, 0, 0} }; @@ -1877,10 +1918,13 @@ litest_parse_argv(int argc, char **argv) if (c == -1) break; switch(c) { - case 'l': + case OPT_FILTER_TEST: + filter_test = optarg; + break; + case OPT_LIST: litest_list_tests(&all_tests); exit(0); - case 'v': + case OPT_VERBOSE: verbose = 1; break; default: diff --git a/test/litest.h b/test/litest.h index b6aaa6f5..47bbe544 100644 --- a/test/litest.h +++ b/test/litest.h @@ -109,26 +109,45 @@ struct libinput *litest_create_context(void); void litest_disable_log_handler(struct libinput *libinput); void litest_restore_log_handler(struct libinput *libinput); -void litest_add(const char *name, void *func, - enum litest_device_feature required_feature, - enum litest_device_feature excluded_feature); -void litest_add_ranged(const char *name, - void *func, - enum litest_device_feature required, - enum litest_device_feature excluded, - const struct range *range); -void -litest_add_for_device(const char *name, - void *func, - enum litest_device_type type); -void litest_add_ranged_for_device(const char *name, +#define litest_add(name_, func_, ...) \ + _litest_add(name_, #func_, func_, __VA_ARGS__) +#define litest_add_ranged(name_, func_, ...) \ + _litest_add_ranged(name_, #func_, func_, __VA_ARGS__) +#define litest_add_for_device(name_, func_, ...) \ + _litest_add_for_device(name_, #func_, func_, __VA_ARGS__) +#define litest_add_ranged_for_device(name_, func_, ...) \ + _litest_add_ranged_for_device(name_, #func_, func_, __VA_ARGS__) +#define litest_add_no_device(name_, func_) \ + _litest_add_no_device(name_, #func_, func_) +#define litest_add_ranged_no_device(name_, func_, ...) \ + _litest_add_ranged_no_device(name_, #func_, func_, __VA_ARGS__) +void _litest_add(const char *name, + const char *funcname, + void *func, + enum litest_device_feature required_feature, + enum litest_device_feature excluded_feature); +void _litest_add_ranged(const char *name, + const char *funcname, + void *func, + enum litest_device_feature required, + enum litest_device_feature excluded, + const struct range *range); +void _litest_add_for_device(const char *name, + const char *funcname, + void *func, + enum litest_device_type type); +void _litest_add_ranged_for_device(const char *name, + const char *funcname, + void *func, + enum litest_device_type type, + const struct range *range); +void _litest_add_no_device(const char *name, + const char *funcname, + void *func); +void _litest_add_ranged_no_device(const char *name, + const char *funcname, void *func, - enum litest_device_type type, const struct range *range); -void litest_add_no_device(const char *name, void *func); -void litest_add_ranged_no_device(const char *name, - void *func, - const struct range *range); extern void litest_setup_tests(void); struct litest_device * litest_create_device(enum litest_device_type which); From 538e98d46f796720abe95fff0d636f67828efe88 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 10:49:13 +1000 Subject: [PATCH 163/165] test: add --filter-device argument Similar to the CK_RUN_CASE environment variable, but it does support fnmatch()-style wildcards, e.g. ./test/test-touchpad --filter-device="synaptics*" Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 11 ++++++++++ test/litest.c | 50 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index 079018c0..757dc86e 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -69,6 +69,17 @@ basic shell-style function name matching. For example: $ ./test/test-touchpad --filter-test="*1fg_tap*" @endcode +The `--filter-device` argument enables selective running of tests through +basic shell-style device name matching. The device names matched are the +litest-specific shortnames, see the output of `--list`. For example: + +@code +$ ./test/test-touchpad --filter-device="synaptics*" +@endcode + +The `--filter-device` argument can be combined with `--list` to show +which devices will be affected. + @section test-verbosity Controlling test output Each test supports the `--verbose` commandline option to enable debugging diff --git a/test/litest.c b/test/litest.c index fb0403db..fb4e1b93 100644 --- a/test/litest.c +++ b/test/litest.c @@ -52,6 +52,7 @@ static int in_debugger = -1; static int verbose = 0; const char *filter_test = NULL; +const char *filter_device = NULL; struct test { struct list node; @@ -237,6 +238,10 @@ litest_add_tcase_no_device(struct suite *suite, struct test *t; const char *test_name = "no device"; + if (filter_device && + fnmatch(filter_device, test_name, 0) != 0) + return; + list_for_each(t, &suite->tests, node) { if (strcmp(t->name, test_name) != 0) continue; @@ -305,24 +310,31 @@ litest_add_tcase(const char *suite_name, excluded == LITEST_DISABLE_DEVICE) { litest_add_tcase_no_device(suite, func, range); } else if (required != LITEST_ANY || excluded != LITEST_ANY) { - while (*dev) { - if (((*dev)->features & required) == required && - ((*dev)->features & excluded) == 0) - litest_add_tcase_for_device(suite, - funcname, - func, - *dev, - range); - dev++; - } - } else { - while (*dev) { + for (; *dev; dev++) { + if (filter_device && + fnmatch(filter_device, (*dev)->shortname, 0) != 0) + continue; + if (((*dev)->features & required) != required || + ((*dev)->features & excluded) != 0) + continue; + + litest_add_tcase_for_device(suite, + funcname, + func, + *dev, + range); + } + } else { + for (; *dev; dev++) { + if (filter_device && + fnmatch(filter_device, (*dev)->shortname, 0) != 0) + continue; + litest_add_tcase_for_device(suite, funcname, func, *dev, range); - dev++; } } } @@ -395,7 +407,11 @@ _litest_add_ranged_for_device(const char *name, assert(type < LITEST_NO_DEVICE); s = get_suite(name); - while (*dev) { + for (; *dev; dev++) { + if (filter_device && + fnmatch(filter_device, (*dev)->shortname, 0) != 0) + continue; + if ((*dev)->type == type) { litest_add_tcase_for_device(s, funcname, @@ -404,7 +420,6 @@ _litest_add_ranged_for_device(const char *name, range); return; } - dev++; } ck_abort_msg("Invalid test device type"); @@ -1900,11 +1915,13 @@ litest_parse_argv(int argc, char **argv) { enum { OPT_FILTER_TEST, + OPT_FILTER_DEVICE, OPT_LIST, OPT_VERBOSE, }; static const struct option opts[] = { { "filter-test", 1, 0, OPT_FILTER_TEST }, + { "filter-device", 1, 0, OPT_FILTER_DEVICE }, { "list", 0, 0, OPT_LIST }, { "verbose", 0, 0, OPT_VERBOSE }, { 0, 0, 0, 0} @@ -1921,6 +1938,9 @@ litest_parse_argv(int argc, char **argv) case OPT_FILTER_TEST: filter_test = optarg; break; + case OPT_FILTER_DEVICE: + filter_device = optarg; + break; case OPT_LIST: litest_list_tests(&all_tests); exit(0); From f3947c0eb9ae61b1dc0d8bc8e8050c75a6e98543 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 11:00:37 +1000 Subject: [PATCH 164/165] test: add --filter-group argument to match test groups (suites) Same as CK_RUN_SUITE, but supports fnmatch-like globs Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 12 ++++++++++-- test/litest.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index 757dc86e..83cae24b 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -77,8 +77,16 @@ litest-specific shortnames, see the output of `--list`. For example: $ ./test/test-touchpad --filter-device="synaptics*" @endcode -The `--filter-device` argument can be combined with `--list` to show -which devices will be affected. +The `--filter-group` argument enables selective running of test groups +through basic shell-style test group matching. The test groups matched are +litest-specific test groups, see the output of `--list`. For example: + +@code +$ ./test/test-touchpad --filter-group="touchpad:*hover*" +@endcode + +The `--filter-device` and `--filter-group` arguments can be combined with +`--list` to show which groups and devices will be affected. @section test-verbosity Controlling test output diff --git a/test/litest.c b/test/litest.c index fb4e1b93..2b1e183c 100644 --- a/test/litest.c +++ b/test/litest.c @@ -53,6 +53,7 @@ static int in_debugger = -1; static int verbose = 0; const char *filter_test = NULL; const char *filter_device = NULL; +const char *filter_group = NULL; struct test { struct list node; @@ -304,6 +305,10 @@ litest_add_tcase(const char *suite_name, fnmatch(filter_test, funcname, 0) != 0) return; + if (filter_group && + fnmatch(filter_group, suite_name, 0) != 0) + return; + suite = get_suite(suite_name); if (required == LITEST_DISABLE_DEVICE && @@ -406,6 +411,10 @@ _litest_add_ranged_for_device(const char *name, assert(type < LITEST_NO_DEVICE); + if (filter_group && + fnmatch(filter_group, name, 0) != 0) + return; + s = get_suite(name); for (; *dev; dev++) { if (filter_device && @@ -1916,12 +1925,14 @@ litest_parse_argv(int argc, char **argv) enum { OPT_FILTER_TEST, OPT_FILTER_DEVICE, + OPT_FILTER_GROUP, OPT_LIST, OPT_VERBOSE, }; static const struct option opts[] = { { "filter-test", 1, 0, OPT_FILTER_TEST }, { "filter-device", 1, 0, OPT_FILTER_DEVICE }, + { "filter-group", 1, 0, OPT_FILTER_GROUP }, { "list", 0, 0, OPT_LIST }, { "verbose", 0, 0, OPT_VERBOSE }, { 0, 0, 0, 0} @@ -1941,6 +1952,9 @@ litest_parse_argv(int argc, char **argv) case OPT_FILTER_DEVICE: filter_device = optarg; break; + case OPT_FILTER_GROUP: + filter_group = optarg; + break; case OPT_LIST: litest_list_tests(&all_tests); exit(0); From fcd1cbe5e5dfdb66fd1136273ba15a66cf40caa5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 May 2015 11:04:13 +1000 Subject: [PATCH 165/165] doc: drop leftovers of Check's selective test running CK_RUN_CASE and CK_RUN_SUITE still work because we're still using check underneath, but it's better to use the arguments. Signed-off-by: Peter Hutterer --- doc/test-suite.dox | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/doc/test-suite.dox b/doc/test-suite.dox index 83cae24b..30dd1579 100644 --- a/doc/test-suite.dox +++ b/doc/test-suite.dox @@ -25,10 +25,10 @@ resulting `/dev/input/eventX` nodes. Some tests require temporary udev rules. @section test-filtering Selective running of tests -Check enables tests to be grouped into suites and test cases, litest uses -test suites as a general feature-specific grouping (e.g. "touchpad:tap") and -instantiates one test case per device. The --list flag shows the list of -suites and tests. +litest's tests are grouped by test groups and devices. A test group is e.g. +"touchpad:tap" and incorporates all tapping-related tests for touchpads. +Each test function is (usually) run with one or more specific devices. +The `--list` commandline argument shows the list of suites and tests. @code $ ./test/test-device --list device:wheel: @@ -52,16 +52,6 @@ uinput by litest. The "no device" entry signals that litest does not instantiate a uinput device for a specific test (though the test itself may instantiate one). -Check provides two filters through environment variables: CK_RUN_SUITE -and CK_RUN_CASE. They may be used independently or combined to narrow -down the set of tests to run. For example: - -@code -$ CK_RUN_SUITE="device:wheel" ./test/test-device -$ CK_RUN_CASE="wheel only" ./test/test-device -$ CK_RUN_SUITE="device:wheel" CK_RUN_CASE="wheel only" ./test/test-device -@endcode - The `--filter-test` argument enables selective running of tests through basic shell-style function name matching. For example: