svn commit: r200241 - head/sys/dev/usb/input

Rui Paulo rpaulo at FreeBSD.org
Mon Dec 7 16:52:59 PST 2009


Author: rpaulo
Date: Tue Dec  8 00:52:59 2009
New Revision: 200241
URL: http://svn.freebsd.org/changeset/base/200241

Log:
  Improve response to multi-touch taps.
  
  Submitted by:	Rohit Grover <rgrover1 at gmail.com>

Modified:
  head/sys/dev/usb/input/atp.c

Modified: head/sys/dev/usb/input/atp.c
==============================================================================
--- head/sys/dev/usb/input/atp.c	Tue Dec  8 00:44:23 2009	(r200240)
+++ head/sys/dev/usb/input/atp.c	Tue Dec  8 00:52:59 2009	(r200241)
@@ -437,7 +437,7 @@ static void          atp_detect_pspans(i
 
 /* 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_
 /* 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 *
 		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_sens
  */
 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 
 				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,
 		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_soft
 	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 ((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;
+			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;
+
+			/* 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_erro
 		 */
 		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)));


More information about the svn-src-all mailing list