linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Rafi Rubin <rafi@seas.upenn.edu>
To: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com,
	jkosina@suse.cz, chatty@enac.fr
Cc: Rafi Rubin <rafi@seas.upenn.edu>
Subject: [PATCH 4/4] hid-ntrig: Contact tracking
Date: Thu, 11 Feb 2010 22:14:08 -0500	[thread overview]
Message-ID: <1265944448-23436-4-git-send-email-rafi@seas.upenn.edu> (raw)
In-Reply-To: <1265944448-23436-3-git-send-email-rafi@seas.upenn.edu>

Added contact tracking to compensate for the lack of tracking in
hardware.  The approach is simplistic and leaves considerable
room for improvement.

Signed-off-by: Rafi Rubin <rafi@seas.upenn.edu>
---
 drivers/hid/hid-ntrig.c |  216 +++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 183 insertions(+), 33 deletions(-)

diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 3da7287..ba092f6 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -64,7 +64,9 @@ struct ntrig_data {
 
 	/* Collected state for 2 full sets of contacts */
 	struct ntrig_contact contacts[NTRIG_MAX_CONTACTS];
+	struct ntrig_contact prev_contacts[NTRIG_MAX_CONTACTS];
 	__u8 contact_count;
+	__u8 prev_contact_count;
 
 	__u8 mt_footer[4];
 	__u8 mt_foot_count;
@@ -117,18 +119,15 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	case HID_UP_DIGITIZER:
 		switch (usage->hid) {
 		/* we do not want to map these for now */
-		case HID_DG_CONTACTID: /* value is useless */
 		case HID_DG_INPUTMODE:
 		case HID_DG_DEVICEINDEX:
-		case HID_DG_CONTACTCOUNT:
 		case HID_DG_CONTACTMAX:
 			return -1;
 
-		/* original mapping by Rafi Rubin */
-		case HID_DG_CONFIDENCE:
-			nt_map_key_clear(BTN_TOOL_DOUBLETAP);
+		case HID_DG_CONTACTID:
+			hid_map_usage(hi, usage, bit, max, EV_ABS,
+					ABS_MT_TRACKING_ID);
 			return 1;
-
 		/* width/height mapped on TouchMajor/TouchMinor/Orientation */
 		case HID_DG_WIDTH:
 			hid_map_usage(hi, usage, bit, max,
@@ -166,43 +165,191 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 	return 0;
 }
 
-static void ntrig_conclude_mt(struct input_dev *input, struct ntrig_data *nd)
+static void ntrig_single_touch_emit(struct input_dev *input,
+				    struct ntrig_data *nd)
 {
-	int i;
-	struct ntrig_contact *contact = &nd->contacts[0];
-
-	/* Emit single touch events */
-	if (contact->confidence) {
+	if (nd->confidence) {
 		switch (nd->contact_count) {
 		case 0:	/* for single touch devices */
 		case 1:
-			input_report_key(nd->st_input, BTN_TOOL_DOUBLETAP, 1);
+			input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
 			break;
 		case 2:
-			input_report_key(nd->st_input, BTN_TOOL_TRIPLETAP, 1);
+			input_report_key(input, BTN_TOOL_TRIPLETAP, 1);
 			break;
 		case 3:
 		default:
-			input_report_key(nd->st_input, BTN_TOOL_QUADTAP, 1);
+			input_report_key(input, BTN_TOOL_QUADTAP, 1);
+		}
+		input_report_key(input, BTN_TOUCH, 1);
+		input_event(input, EV_ABS, ABS_X, nd->x);
+		input_event(input, EV_ABS, ABS_Y, nd->y);
+	} else {
+		/* No active fingers, clear all state */
+		input_report_key(input, BTN_TOUCH, 0);
+		input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
+		input_report_key(input, BTN_TOOL_TRIPLETAP, 0);
+		input_report_key(input, BTN_TOOL_QUADTAP, 0);
+	}
+}
+
+/*
+ * Spatial comparison of two points.  If the difference
+ * is within the given thresholds they are treated as the
+ * same point.
+ */
+#define nt_same_point(a, b, max_dx, max_dy) ( \
+		(abs(a.x - b.x) <= max_dx) && \
+		(abs(a.y - b.y) <= max_dy))
+
+/*
+ * To verify a new contact matches a contact in the previous
+ * group, ensure both are valid then check spatial correlation.
+ */
+#define nt_match_points(nd, new, old) (nd->contacts[new].confidence && \
+		nd->prev_contacts[old].confidence && \
+		nt_same_point(nd->contacts[new], nd->prev_contacts[old], \
+			nd->max_width, nd->max_height))
+
+/*
+ * After an older contact is identified as a match nt_map_match updates
+ * the newer point as well as the contact map
+ */
+static inline void nt_map_match(struct ntrig_data *nd, __s8 *contact_map,
+		int new, int old)
+{
+	nd->contacts[new].logical_id = nd->prev_contacts[old].logical_id;
+	contact_map[nd->contacts[new].logical_id] = new;
+}
+
+static void ntrig_conclude_mt(struct input_dev *input, struct ntrig_data *nd)
+{
+	__s8 contact_map[nd->max_contacts];
+	int i, j, k;
+	int matched = 0;
+	int first_free_id = 0;
+	int count = nd->contact_count;
+	int prev_count = nd->prev_contact_count;
+	struct ntrig_contact *contact;
+
+	/* If the previous state is corrupted, discard it. */
+	if (nd->prev_contact_count >= NTRIG_MAX_CONTACTS) {
+		printk(KERN_ERR
+		       "N-Trig previous state corrupted, discarding\n");
+		nd->prev_contact_count = 0;
+		prev_count = 0;
+	}
+
+	/* Under some circumstances an empty group is emitted with an invalid
+	 * contact 0 and contact count of 1. */
+	if (count && (!nd->contacts[0].confidence)) {
+		count = 0;
+		nd->contact_count = 0;
+	}
+
+	/* Contact tracking logic */
+
+	/* Initialize and empty logical id map */
+	for (i = 0; i < nd->max_contacts; i++)
+		contact_map[i] = -1;
+
+	/*
+	 * Phase 1: Identify which contacts seem to match
+	 * those with the same physical id from the previous group.
+	 * This should be the most common case during long touch
+	 * action. */
+	for (i = 0; i < count && i < prev_count; i++) {
+		if (nt_match_points(nd, i, i)) {
+			nt_map_match(nd, contact_map, i, i);
+			matched++;
+		} else
+			nd->contacts[i].logical_id = -1;
+	}
+
+	/*
+	 * Phase 2: Find corresponding contacts when the incoming
+	 * order has changed.
+	 */
+	for (i = 0; i < count && matched < count; i++) {
+		for (j = 0; j < prev_count &&
+		     (nd->contacts[i].logical_id < 0); j++) {
+
+			/* Check the map to avoid reusing an old contact
+			 * for multiple current contacts */
+			if ((contact_map[nd->prev_contacts[j].logical_id] < 0)
+			    && nt_match_points(nd, i, j)) {
+				nt_map_match(nd, contact_map, i, j);
+				matched++;
+			}
 		}
-		input_report_key(nd->st_input, BTN_TOUCH, 1);
-		input_event(nd->st_input, EV_ABS, ABS_X, contact->x);
-		input_event(nd->st_input, EV_ABS, ABS_Y, contact->y);
+	}
+
+	/*
+	 * Phase 3: New or unidentied contacts are assigned logical ids.
+	 */
+	for (i = 0; i < count && matched < count; i++) {
+		/* Ignore points that are already mapped */
+		if ((nd->contacts[i].confidence
+		     && nd->contacts[i].logical_id < 0)) {
+			/* find the first available logical id */
+			while (contact_map[first_free_id] >= 0
+			       && first_free_id < nd->max_contacts)
+				first_free_id++;
+			if (first_free_id >= nd->max_contacts) {
+				printk(KERN_ERR
+				       "hid-ntrig: exceeded contacts limit\n");
+				break;
+			}
+			nd->contacts[i].logical_id = first_free_id;
+			contact_map[first_free_id++] = i;
+			matched++;
+		}
+	}
+
+	k = -1;			/* Lowest id contact */
+	j = -1;			/* Highest id contact */
+	if (nd->emit_ghosts < 2) {
+		for (i = 0; i < nd->max_contacts; i++)
+			if (contact_map[i] >= 0) {
+				j = i;
+				if (k < 0)
+					k = i;
+			}
 	} else {
-		input_report_key(nd->st_input, BTN_TOUCH, 0);
-		input_report_key(nd->st_input, BTN_TOOL_DOUBLETAP, 0);
-		input_report_key(nd->st_input, BTN_TOOL_TRIPLETAP, 0);
-		input_report_key(nd->st_input, BTN_TOOL_QUADTAP, 0);
+		j = nd->max_contacts - 1;
+		for (i = 0; i < nd->max_contacts; i++)
+			if (contact_map[i] >= 0) {
+				k = i;
+				break;
+			}
+	}
+
+	/* Update the classic touchscreen state */
+	if (count) {
+		nd->x = nd->contacts[contact_map[k]].x;
+		nd->y = nd->contacts[contact_map[k]].y;
+		nd->confidence = nd->contacts[contact_map[k]].confidence;
+		ntrig_single_touch_emit(nd->st_input, nd);
+	} else {
+		/* Hit the end of activity, clear state */
+		nd->confidence = 0;
+		ntrig_single_touch_emit(nd->st_input, nd);
 	}
 
 	/* Multitouch doesn't need to send an end of activity notice. */
-	if (!(nd->contact_count && nd->contacts[0].confidence))
+	if (!(count && nd->contacts[0].confidence)) {
+		/* Only need to tag this group as empty, don't have
+		 * to worry about preserving all the empty state */
+		nd->prev_contact_count = 0;
 		return;
+	}
 
 	/* Emit multitouch events */
-	for (i = 0; i <= nd->max_contacts && i < NTRIG_MAX_CONTACTS; i++) {
-		if (nd->contacts[i].confidence) {
-			struct ntrig_contact *contact = &nd->contacts[i];
+	for (i = 0; i <= j; i++) {
+		/* Valid contact, send real values */
+		if (contact_map[i] >= 0
+		    && nd->contacts[contact_map[i]].confidence) {
+			contact = &nd->contacts[contact_map[i]];
 			input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
 			input_event(input, EV_ABS, ABS_MT_POSITION_X,
 					contact->x);
@@ -226,6 +373,11 @@ static void ntrig_conclude_mt(struct input_dev *input, struct ntrig_data *nd)
 			input_mt_sync(input);
 		}
 	}
+
+	/* Age the current state to previous. */
+	for (i = 0; i < count; i++)
+		nd->prev_contacts[i] = nd->contacts[i];
+	nd->prev_contact_count = nd->contact_count;
 }
 
 /*
@@ -234,7 +386,7 @@ static void ntrig_conclude_mt(struct input_dev *input, struct ntrig_data *nd)
  * decide whether we are in multi or single touch mode
  * and call input_mt_sync after each point if necessary
  */
-static int ntrig_event (struct hid_device *hid, struct hid_field *field,
+static int ntrig_event(struct hid_device *hid, struct hid_field *field,
 		                        struct hid_usage *usage, __s32 value)
 {
 	struct input_dev *input = field->hidinput->input;
@@ -244,7 +396,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
 	if (field->application == HID_DG_PEN)
 		return 0;
 
-        if (hid->claimed & HID_CLAIMED_INPUT) {
+	if (hid->claimed & HID_CLAIMED_INPUT) {
 		switch (usage->hid) {
 		case 0xff000001:
 			/* Tag indicating the start of a multitouch group */
@@ -275,8 +427,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
 			 * to emit a normal (X, Y) position
 			 */
 			if (!nd->reading_mt) {
-				input_event(input, EV_ABS, ABS_X, nd->x);
-				input_event(input, EV_ABS, ABS_Y, nd->y);
+				ntrig_single_touch_emit(input, nd);
 			}
 			break;
 		case 0xff000002:
@@ -307,8 +458,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
 
 			/* If the contact was invalid */
 			if (!(nd->confidence && nd->mt_footer[0])
-					|| nd->w <= 250
-					|| nd->h <= 190) {
+					|| nd->w <= 250 || nd->h <= 190) {
 				nd->contacts[nd->id].x = 0;
 				nd->contacts[nd->id].y = 0;
 				nd->contacts[nd->id].confidence = 0;
@@ -373,6 +523,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	nd->id = 0;
 	nd->reading_mt = 0;
 	nd->contact_count = 0;
+	nd->prev_contact_count = 0;
 	nd->max_width = 0x500;
 	nd->max_height = 0x500;
 	nd->max_contacts = 5;
@@ -392,7 +543,6 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto err_free;
 	}
 
-
 	list_for_each_entry(hidinput, &hdev->inputs, list) {
 		input = hidinput->input;
 		switch (hidinput->report->field[0]->application) {
-- 
1.6.6.1


  reply	other threads:[~2010-02-12  3:14 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-12  0:19 [PATCH 1/4] HID: hid-ntrig add multi input quirk and clean up Rafi Rubin
2010-02-12  0:19 ` [PATCH 2/4] hid-ntrig.c: removed unnecessary tool switching Rafi Rubin
2010-02-12  0:19   ` [PATCH 3/4] hid-ntrig.c Split multi and single touch Rafi Rubin
2010-02-12  0:19     ` [PATCH 4/4] hid-ntrig: Contact tracking Rafi Rubin
2010-02-12  0:42     ` [PATCH 3/4] hid-ntrig.c Split multi and single touch Dmitry Torokhov
2010-02-12  3:10       ` Rafi Rubin
2010-02-12  8:09         ` Dmitry Torokhov
2010-02-12 19:56           ` Rafi Rubin
2010-02-12 10:41       ` Jiri Kosina
2010-02-12  0:36 ` [PATCH 1/4] HID: hid-ntrig add multi input quirk and clean up Dmitry Torokhov
2010-02-12  0:45   ` Rafi Rubin
2010-02-12  1:03   ` Rafi Rubin
2010-02-12  1:20     ` Dmitry Torokhov
2010-02-12  0:37 ` Rafi Rubin
2010-02-12  3:14 ` Rafi Rubin
2010-02-12  3:14   ` [PATCH 2/4] hid-ntrig.c: removed unnecessary tool switching Rafi Rubin
2010-02-12  3:14     ` [PATCH 3/4] hid-ntrig.c Split multi and single touch Rafi Rubin
2010-02-12  3:14       ` Rafi Rubin [this message]
2010-02-20  8:29         ` [PATCH 4/4] hid-ntrig: Contact tracking Mohamed Ikbel Boulabiar
     [not found]         ` <45cc95261002200025m378e1a80rec09bde5673a6060@mail.gmail.com>
2010-02-20 17:48           ` Rafi Rubin
2010-02-12  8:13       ` [PATCH 3/4] hid-ntrig.c Split multi and single touch Dmitry Torokhov
2010-02-12 23:16         ` Rafi Rubin
2010-02-13  2:13           ` [PATCH] hid-ntrig.c Multitouch cleanup and fix Rafi Rubin
2010-02-13  2:24             ` Rafi Rubin
2010-02-16 12:50               ` Jiri Kosina
2010-03-09 21:01                 ` Henrik Rydberg
2010-03-09 21:17                   ` Rafi Rubin
2010-03-09 21:19                     ` Jiri Kosina
2010-03-09 22:03                       ` Stéphane Chatty
2010-03-09 22:25                         ` Henrik Rydberg
2010-03-09 22:42                           ` Mohamed Ikbel Boulabiar
2010-03-09 23:08                             ` Henrik Rydberg
2010-03-09 23:17                               ` Dmitry Torokhov
2010-03-09 23:26                                 ` Henrik Rydberg
2010-03-11  4:30                             ` Peter Hutterer
2010-03-11  5:36                               ` Mohamed Ikbel Boulabiar
2010-03-11  6:25                                 ` Peter Hutterer
2010-03-11  9:42                               ` Stéphane Chatty
2010-03-09 22:27                         ` Dmitry Torokhov
2010-03-09 22:32                           ` Rafi Rubin
2010-03-09 22:54                           ` Stéphane Chatty
2010-03-09 22:12                       ` Rafi Rubin
2010-03-09 22:39                         ` Henrik Rydberg
2010-03-09 21:59                     ` Henrik Rydberg
2010-03-09 22:11                       ` Stéphane Chatty
2010-03-09 22:29                         ` Henrik Rydberg
2010-03-09 22:44                           ` Stéphane Chatty
2010-03-09 22:27                       ` Rafi Rubin
2010-03-09 23:23                         ` Henrik Rydberg
2010-03-09 23:38                           ` Rafi Rubin
2010-03-09 23:42                             ` Dmitry Torokhov
2010-03-10  0:32                               ` Rafi Rubin
2010-03-10  3:47                                 ` Dmitry Torokhov
2010-03-10  4:40                                   ` Rafi Rubin
2010-03-10  8:38                                     ` [PATCH] hid: ntrig touch events rafi
2010-03-10 15:04                                       ` Jiri Kosina
2010-03-18 20:19                                         ` Rafi Rubin
2010-03-19  8:44                                           ` Jiri Kosina
2010-03-19 14:12                                             ` Rafi Rubin
2010-02-16 12:49     ` [PATCH 2/4] hid-ntrig.c: removed unnecessary tool switching Jiri Kosina
2010-02-12  3:15   ` [PATCH 1/4] HID: hid-ntrig add multi input quirk and clean up Rafi Rubin
2010-02-16 12:49   ` Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1265944448-23436-4-git-send-email-rafi@seas.upenn.edu \
    --to=rafi@seas.upenn.edu \
    --cc=chatty@enac.fr \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).