Improve response to multi-touch taps.

Submitted by:	Rohit Grover <rgrover1 at gmail.com>
This commit is contained in:
Rui Paulo 2009-12-08 00:52:59 +00:00
parent c4dbd41f5d
commit 64e5a27754

View File

@ -437,7 +437,7 @@ static void atp_detect_pspans(int *, u_int, u_int, atp_pspan *,
/* movement detection */
static boolean_t atp_match_stroke_component(atp_stroke_component *,
const atp_pspan *);
const atp_pspan *, atp_stroke_type);
static void atp_match_strokes_against_pspans(struct atp_softc *,
atp_axis, atp_pspan *, u_int, u_int);
static boolean_t atp_update_strokes(struct atp_softc *,
@ -458,6 +458,7 @@ static boolean_t atp_compute_stroke_movement(atp_stroke *);
/* tap detection */
static __inline void atp_setup_reap_time(struct atp_softc *, struct timeval *);
static void atp_reap_zombies(struct atp_softc *, u_int *, u_int *);
static void atp_convert_to_slide(struct atp_softc *, atp_stroke *);
/* updating fifo */
static void atp_reset_buf(struct atp_softc *sc);
@ -725,7 +726,7 @@ atp_interpret_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) {
arr[i] = sensor_data[di];
arr[i+8] = sensor_data[di+2];
if (axis == X && num > 16)
if (axis == X && num > 16)
arr[i+16] = sensor_data[di+40];
}
@ -879,23 +880,43 @@ atp_detect_pspans(int *p, u_int num_sensors,
*/
static boolean_t
atp_match_stroke_component(atp_stroke_component *component,
const atp_pspan *pspan)
const atp_pspan *pspan, atp_stroke_type stroke_type)
{
int delta_mickeys = pspan->loc - component->loc;
int delta_mickeys;
u_int min_pressure;
delta_mickeys = pspan->loc - component->loc;
if (abs(delta_mickeys) > atp_max_delta_mickeys)
return (FALSE); /* the finger span is too far out; no match */
component->loc = pspan->loc;
/*
* A sudden and significant increase in a pspan's cumulative
* pressure indicates the incidence of a new finger
* contact. This usually revises the pspan's
* centre-of-gravity, and hence the location of any/all
* matching stroke component(s). But such a change should
* *not* be interpreted as a movement.
*/
if (pspan->cum > ((3 * component->cum_pressure) >> 1))
delta_mickeys = 0;
component->cum_pressure = pspan->cum;
if (pspan->cum > component->max_cum_pressure)
component->max_cum_pressure = pspan->cum;
/*
* If the cumulative pressure drops below a quarter of the max,
* then disregard the component's movement.
* Disregard the component's movement if its cumulative
* pressure drops below a fraction of the maximum; this
* fraction is determined based on the stroke's type.
*/
if (component->cum_pressure < (component->max_cum_pressure >> 2))
if (stroke_type == ATP_STROKE_TOUCH)
min_pressure = (3 * component->max_cum_pressure) >> 2;
else
min_pressure = component->max_cum_pressure >> 2;
if (component->cum_pressure < min_pressure)
delta_mickeys = 0;
component->delta_mickeys = delta_mickeys;
@ -930,7 +951,8 @@ atp_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
continue; /* skip matched pspans */
if (atp_match_stroke_component(
&stroke->components[axis], &pspans[j])) {
&stroke->components[axis], &pspans[j],
stroke->type)) {
/* There is a match. */
stroke->components[axis].matched = TRUE;
@ -1065,19 +1087,23 @@ atp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
for (i = 0; i < sc->sc_n_strokes; i++) {
atp_stroke *stroke = &sc->sc_strokes[i];
printf(" %s%clc:%u,dm:%d,pnd:%d,mv:%d%c"
",%clc:%u,dm:%d,pnd:%d,mv:%d%c",
printf(" %s%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c"
",%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c",
(stroke->flags & ATSF_ZOMBIE) ? "zomb:" : "",
(stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
stroke->components[X].loc,
stroke->components[X].delta_mickeys,
stroke->components[X].pending,
stroke->components[X].cum_pressure,
stroke->components[X].max_cum_pressure,
stroke->components[X].movement,
(stroke->type == ATP_STROKE_TOUCH) ? ']' : '>',
(stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
stroke->components[Y].loc,
stroke->components[Y].delta_mickeys,
stroke->components[Y].pending,
stroke->components[Y].cum_pressure,
stroke->components[Y].max_cum_pressure,
stroke->components[Y].movement,
(stroke->type == ATP_STROKE_TOUCH) ? ']' : '>');
}
@ -1218,51 +1244,94 @@ atp_advance_stroke_state(struct atp_softc *sc, atp_stroke *stroke,
if (atp_compute_stroke_movement(stroke))
*movement = TRUE;
if (stroke->type != ATP_STROKE_TOUCH)
return;
/* Convert touch strokes to slides upon detecting movement or age. */
if (stroke->type == ATP_STROKE_TOUCH) {
struct timeval tdiff;
if (stroke->cum_movement >= atp_slide_min_movement) {
atp_convert_to_slide(sc, stroke);
} else {
/* If a touch stroke is found to be older than the
* touch-timeout threshold, it should be converted to
* a slide; except if there is a co-incident sibling
* with a later creation time.
*
* When multiple fingers make contact with the
* touchpad, they are likely to be separated in their
* times of incidence. During a multi-finger tap,
* therefore, the last finger to make
* contact--i.e. the one with the latest
* 'ctime'--should be used to determine how the
* touch-siblings get treated; otherwise older
* siblings may lapse the touch-timeout and get
* converted into slides prematurely. The following
* loop determines if there exists another touch
* stroke with a larger 'ctime' than the current
* stroke (NOTE: zombies with a larger 'ctime' are
* also considered) .
*/
/* Compute the stroke's age. */
getmicrotime(&tdiff);
if (timevalcmp(&tdiff, &stroke->ctime, >))
timevalsub(&tdiff, &stroke->ctime);
else {
/*
* If we are here, it is because getmicrotime
* reported the current time as being behind
* the stroke's start time; getmicrotime can
* be imprecise.
*/
tdiff.tv_sec = 0;
tdiff.tv_usec = 0;
u_int i;
for (i = 0; i < sc->sc_n_strokes; i++) {
if ((&sc->sc_strokes[i] == stroke) ||
(sc->sc_strokes[i].type != ATP_STROKE_TOUCH))
continue;
if (timevalcmp(&sc->sc_strokes[i].ctime,
&stroke->ctime, >))
break;
}
if (i == sc->sc_n_strokes) {
/* Found no other touch stroke with a larger 'ctime'. */
struct timeval tdiff;
if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
(tdiff.tv_usec > atp_touch_timeout)) ||
(stroke->cum_movement >= atp_slide_min_movement)) {
/* Switch this stroke to being a slide. */
stroke->type = ATP_STROKE_SLIDE;
/* Are we at the beginning of a double-click-n-drag? */
if ((sc->sc_n_strokes == 1) &&
((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
timevalcmp(&stroke->ctime, &sc->sc_reap_time, >)) {
struct timeval delta;
struct timeval window = {
atp_double_tap_threshold / 1000000,
atp_double_tap_threshold % 1000000
};
delta = stroke->ctime;
timevalsub(&delta, &sc->sc_reap_time);
if (timevalcmp(&delta, &window, <=))
sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
/* Compute the stroke's age. */
getmicrotime(&tdiff);
if (timevalcmp(&tdiff, &stroke->ctime, >))
timevalsub(&tdiff, &stroke->ctime);
else {
/*
* If we are here, it is because getmicrotime
* reported the current time as being behind
* the stroke's start time; getmicrotime can
* be imprecise.
*/
tdiff.tv_sec = 0;
tdiff.tv_usec = 0;
}
if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
(tdiff.tv_usec >=
(atp_touch_timeout % 1000000))))
atp_convert_to_slide(sc, stroke);
}
}
}
/* Switch a given touch stroke to being a slide. */
void
atp_convert_to_slide(struct atp_softc *sc, atp_stroke *stroke)
{
stroke->type = ATP_STROKE_SLIDE;
/* Are we at the beginning of a double-click-n-drag? */
if ((sc->sc_n_strokes == 1) &&
((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
timevalcmp(&stroke->ctime, &sc->sc_reap_time, >)) {
struct timeval delta;
struct timeval window = {
atp_double_tap_threshold / 1000000,
atp_double_tap_threshold % 1000000
};
delta = stroke->ctime;
timevalsub(&delta, &sc->sc_reap_time);
if (timevalcmp(&delta, &window, <=))
sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
}
}
/*
* Terminate a stroke. While SLIDE strokes are dropped, TOUCH strokes
* are retained as zombies so as to reap all their siblings together;
@ -1723,7 +1792,7 @@ atp_intr(struct usb_xfer *xfer, usb_error_t error)
*/
status_bits = sc->sensor_data[sc->sc_params->data_len - 1];
if ((sc->sc_params->prot == ATP_PROT_GEYSER3 &&
(status_bits & ATP_STATUS_BASE_UPDATE)) ||
(status_bits & ATP_STATUS_BASE_UPDATE)) ||
!(sc->sc_state & ATP_VALID)) {
memcpy(sc->base_x, sc->cur_x,
sc->sc_params->n_xsensors * sizeof(*(sc->base_x)));