Add support for Apple Magic Trackpad

Apple Magic Trackpad can track individual touch contact but
its driver follows MT-A protocol instead of MT-B protocol.
Specifically, the driver reports event ABS_MT_TRACKING_ID as
the indication of state machine change, instead of reporting
ABS_MT_SLOT event.

This CL makes mtplot work with Apple Magic Trackpad by:
1. Current slot is updated with new ABS_MT_TRACKING_ID.
2. 0 touch major value means current slot (touch contact) is
   removed.
3. Use a touch_major value for drawing since the touchpad
   does not report pressure value.

BUG=chromium-os:29435
TEST=Connect Stumpy with Apple Magic Trackpad and check that
     mtplot works.

Change-Id: Iea827ff36590b7edf9602ca6a3db60c00f0efc66
diff --git a/mtplot.c b/mtplot.c
index d6c5174..9384337 100644
--- a/mtplot.c
+++ b/mtplot.c
@@ -461,8 +461,11 @@
 static int y_max = 0;
 static int pressure_min = 0;
 static int pressure_max = 255;
+static int touch_major_min = 0;
+static int touch_major_max = 255;
 
 static bool semi_mt_device = false;
+static bool has_mt_slot = false;
 
 static unsigned int w_width;
 static unsigned int w_height;
@@ -527,13 +530,21 @@
   unsigned int width, height;
   unsigned long forecolor;
 
-  if ((s->track_id == -1) || (s->pressure == 0))
+  if (s->track_id == -1)
     return;
 
-  // TODO: Get really fancy and use touch_minor & orientation
-  // TODO: Get really really fancy, and use 'width_*'
-  width = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
-  height = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
+  if (s->pressure) {
+    // TODO: Get really fancy and use touch_minor & orientation
+    // TODO: Get really really fancy, and use 'width_*'
+    width = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
+    height = 50 * (s->pressure - pressure_min) / (pressure_max - pressure_min);
+  } else {
+    // In case we don't have pressure reading, use touch_major instead.
+    width = 50 * (s->touch_major - touch_major_min) /
+        (touch_major_max - touch_major_min);
+    height = 50 * (s->touch_major - touch_major_min) /
+        (touch_major_max - touch_major_min);
+  }
 
   // TODO: Update x/y_min/max on window resize
   x = (s->x - x_min) * w_width / (x_max - x_min) - (width - 1) / 2;
@@ -584,6 +595,23 @@
   }
 }
 
+// Associate the track ID with slot number.
+static int track_id_to_slot(struct mt_state *s, int track_id) {
+  int i;
+  // For existing track ID, return its associated slot.
+  for (i = slot_min; i <= slot_max; i++)
+    if (s->slot[i].track_id == track_id)
+      return i;
+
+  // For new track ID, return a free slot.
+  for (i = slot_min; i <= slot_max; i++)
+    if (s->slot[i].track_id == -1)
+      return i;
+
+  perror("No free slot for for new track_id");
+  return slot_min;
+}
+
 static void ProcessAbs(struct mt_state *s, struct input_event *e) {
   struct mt_slot *slot = &s->slot[s->current];
 
@@ -592,6 +620,10 @@
       s->current = e->value;
       break;
     case ABS_MT_TOUCH_MAJOR:
+      // If there is no ABS_MT_SLOT support, use a 0 touch major as
+      // the indication of removed touch contact.
+      if (!has_mt_slot && e->value == 0)
+        slot->track_id = -1;
       slot->touch_major = e->value;
       break;
     case ABS_MT_TOUCH_MINOR:
@@ -618,6 +650,13 @@
     case ABS_MT_BLOB_ID:
       break;
     case ABS_MT_TRACKING_ID:
+      // If there is no ABS_MT_SLOT support, use ABS_MT_TRACKING_ID as
+      // replacement. If new ABS_MT_TRACKING_ID is received, update the
+      // current slot to be the one associated with the new track ID.
+      if (!has_mt_slot && e->value >= 0) {
+        s->current = track_id_to_slot(s, e->value);
+        slot = &s->slot[s->current];
+      }
       slot->track_id = e->value;
       break;
     case ABS_MT_PRESSURE:
@@ -723,6 +762,8 @@
           pressure_min = abs[k];
         else if (axis == ABS_PRESSURE)
           pressure_min = abs[k];
+        else if (axis == ABS_MT_TOUCH_MAJOR)
+          touch_major_min = abs[k];
       } else if (k == 2) {
         if (axis == ABS_MT_SLOT)
           slot_max = abs[k];
@@ -734,6 +775,8 @@
           pressure_max = abs[k];
         else if (axis == ABS_PRESSURE)
           pressure_max = abs[k];
+        else if (axis == ABS_MT_TOUCH_MAJOR)
+          touch_major_max = abs[k];
       }
     }
 }
@@ -794,8 +837,11 @@
         if (test_bit(j, bit[i])) {
           printf("    Event code %d (%s)\n", j,
                  names[i] ? (names[i][j] ? names[i][j] : "?") : "?");
-          if (i == EV_ABS)
+          if (i == EV_ABS) {
             PrintAbsData(fd, j);
+            if (j == ABS_MT_SLOT)
+              has_mt_slot = true;
+          }
         }
     }