/!\ see also /driver for a small driver I wrote.

/!\ Note that this documents only the first revision of the touchpad. The second revision (October 2005 powerbooks) is supported in appletouch as well, while the third revision (Macbook Pro) isn't supported yet because the device reports actual HID events, but these are low resolution and not too useful.

Here's what I know about the touchpad so far.

The touchpad claims to be a HID device but doesn't really send HID events as far as I can tell, but rather its own transfers.

transferred data

The trackpad continually transfers an 81 byte block via a USB interrupt transfer.

button

The last byte of the transferred 81 bytes indicates the button status, it is 0x01 if the button is pressed, 0x00 otherwise.

touch

The other 80 bytes are split into two 40 byte sections, the latter always being zerofilled. The following discusses the first 40 byte block of the transfer.

Let me introduce the following mapping function (written in python notation):

def map(bytenumber):
  return bytenumber % 5, bytenumber/5

Then, in X direction, the touchpad has first the sensors with first component being even, in lexical order:

(2,0),(2,1),(2,2),....,(2,7),(4,0),...(4,7)

Same in Y direction with the first components 1 and 3. I'll refer to them as X[0] through X[15] and Y[0] through Y[15].

Those values with first component 0 seem to signify some other level that only covers the left part of the touchpad in X direction, but are less sensitive. I haven't used them.

Now, all these values seem to be potentials, but only their change is interesting.

So now consider the following function:

   1 def accu(current_vector, old_vector, accu_vector):
   2   # current_vector is either X or Y
   3   # old_vector is None if no old values are known,
   4   # otherwise a set of old values
   5   # we change accu_vector which must have length 16, all initialised to 0
   6 
   7   if old_vector is None:
   8     for i in range(16): old_vector[i] = current_vector[i]
   9     return
  10   for i in range(16):
  11     tmp = old_vector[i] - current_vector[i]
  12     if tmp > 127: tmp -= 256
  13     if tmp < -127: tmp += 256
  14     accu_vector[i] -= tmp

In the normal state, accu_vector will be 0, and only fluctuate slightly due to the touchpad characteristics. When you touch it though, these values increase depending on where the finger is in X and Y directions. That can be used to track the position of the finger. We used an approach that calculates the finger position by weighing:

   1 half_weight = sum(accu_vector)
   2 pos = None
   3 i = 0
   4 acc = 0
   5 if half_weight > threshold/2: # get rid of fluctuations
   6   while acc + accu_vector[i] < half_weight:
   7     acc += accu_vector[i]
   8     i += 1
   9   val = i + (half_weight - acc)/(0.0+accu_vector[i]) # weigh inside one of the 16 areas
  10 
  11 # val contains None if no finger or the current position

This snippet calculates the finger position for one direction.

Now this this you can implement pretty much everything you need. I recommend threshold = 20, but we're thinking of a different way to detect the finger. Also, multiple fingers are reported correctly, if there are two fingers on the touchpad you don't know on which of the 4 positions they are (unless this data is sent and we haven't seen it yet).