ScrollPad: Hacking the iBooks TrackPad in Mac OS X

News (Sep. 2003):
Alex Harper (who had built the scrolling features into uControl) has taken the info on extended modes on apple trackpads and built a whole new driver around it. His SideTrack is at its first public beta right now and already more feature rich and robust than my hack (and also has a GUI installer and control panel).
Better get his SideTrack instead of my hack for a robust and actively maintained scrolling-trackpad.


I've allways been thinking that it must be possible to create some fancy hacks with my iBook's built in TrackPad (or TouchPad - whatever you want to call it) - if only i could get it to spit out more detailed information than the standard mouse emulation it performs by default.

The TrackPad built into the iBook is a ADB (Apple Desktop Bus) device that has a built in Microcontroller for talking the ADB protocol, interpreting the sensory input, translating it into relative mouse motion information and reckognizing some basic gestures (short taps can be interpreted as clicks, a tap-and-a-half can be interpreted as the button-down event that preceeds a drag operation).

But what i wanted was the absolute position of the Finger on the TouchPad so i could reckognize hotspots within the driver and do different things based on where, when and how the motion is reckognized. So i started seaching the Web, Usenet, Apples Developer Center, ... only to find: nothing!

Well - nothing specific for the TouchPad - but still some interesting reads:

On the way i found some other information on ADB devices (especially notebook keyboards) that might be useful later on

Next i had a deeper look at Apples ADB Mouse Driver which is published as part of Open Darwin. You can check it out of their CVS from the commandline like this (type anoncvs when asked for a password):
  mkdir opendarwin
  cd opendarwin
  cvs -D :pserver:anonymous@anoncvs.opendarwin.org:/Volumes/src/cvs/od login   
  cvs checkout src/AppleADBMouse/
  open src/AppleADBMouse/AppleADBMouse.pbproj


which should get the sources to your local disk and open the project for the mouse driver kernel extension in Project Builder.

Then i started hacking around in the driver and loading it into the running system. To do that, you have to first unload the current ADB mouse driver and then load the fresh one (as root):

  sudo su -
  cd opendarwin/src/AppleADBMouse/build
  kextunload /System/Library/Extensions/AppleADBMouse.kext
  chown -R root.wheel ./AppleADBMouse.kext
  kextload ./AppleADBMouse.kext
  chown -R <username> ./AppleADBMouse.kext

And then it was hacking and watching the traces i put into the code (IOLog() behaves much like printf()) in an open console.

First, i tried configuring the TrackPad to different handler IDs - since the space-alien document seemed to suggest this as the primary means of changing a devices behavior. I got the following acceptable IDs:

All other handler IDs were rejected by the TouchPad.

Next, i had a closer look at the methods AppleADBMouseType4::start(), AppleADBMouseType4::enableEnhancedMode() and AppleADBMouseType4::setParamProperties() in Apples driver implementation (they contain some hints about special Panther Settings by the way - but no actually differnt code...):

First, the system decides, if it has a W-Enhanced TrackPad by looking for an indicator for 4 Buttons present. My iBook doesn't have them. Instead it reports 2: the physical button is number one and the simulated button (the one activated by tapping) is number two. It seems, W-enhanced TrackPads report additional flags (Palm-detected, outzone) via additional pseudo-buttons. Incidently, that is an exact feature of the Synaptics TouchPad whose technical docs i mentioned above. The even call it W-Mode...

Anyway: next the code 'enables enhanced mode' - that is: activate a mode that can potentially deal with tap-click and tab/2-drag by first writing 0x0D into byte 6 of register 2. The original value is 0x03. In that mode it behaves just like a plain mouse and does not deal with taps. To actually activate these features, additional flags have to be set in register 1.

Next experiment was writing different values into this byte 6 of register 2. And BOOM! That did it!
Writing 0x00 into this location turned the contents of register 0 (the data reported for every action on the pad) into something that had nothing to do with relative motion anymore. After some fiddling, i figured out, it was (nearly) the encoding, the Synaptics TouchPad from above used for absolute positioning. And you even get Z-Axis data: an indicator, of how much the capacitive measure of the pad is activated. It does not really report pressure but rather the area covered by skin.

So here is the summary:

Register 0:
2, 5 or 8 bytes of motion data - depending on the mode.
in absolute mode:
Byte/Bit76543210
0phys. btn. (inv. logic)bit 0..6 of X-axis data
1allways 1bit 0..6 of Y-axis data
2 allways 1bit 7..9 of y-axis data allways 1bit 7..9 of x-axis data
3 allways 1bit 10..12 of y-axis data allways 1bit 10..12 of x-axis data
4 allways 1bit 0..2 of z-axis data allways 1bit 3..5 of z-axis data

Register1:
bytevaluer/wpurpose
00x74 ='t'rMagic id
tpad
10x70 ='p'r
20x61 ='a'r
30x64 ='d'r
40x01r?
50x90r?
60x03rw 0x00: absolute mode,
0x03: plain relative mode
0x0D: enhanced relative mode
70x02r?

And finally: here is what is known about Register 2
(which only makes sense in enhanced relative mode):
byte \ bit 7 6 5 4 3 2 1 0
0 enable soft click tap detection time (unit?) def.Val: 0x19
1 enable soft drag ? defVal = 0x14
2 ? defVal = 0x19
3 enable tap to drop downtime (?) defVal = 0xb2
4 ? defVal = 0xb2
5 ? defVal = 0x8a
6 ? defVal = 0x1b
7 ? defVal = 0x19W-Mode?


Implementation:

Armed with that knowledge i could go forth and create my own replacement for the AppleADBMouse driver. My version can detect a hot scrolling area (usually the right end of the touchpad) and issue scroll events when the finger touched down in that area and is subsequently moved up and down.

it installs a sysctl to configure its features. It also uses the standard mouse config options thru the usual mouse preference pane (enable tapping and dragging - tap-to-drop is not implemented yet since i don't use it). The sysctl scrollpad.activate knows the following values (ORed together):
bitvaluemeaningannotation
01scrollPad mode active bit 0 and 1 are exclusive - if neither is set, the default touchpad behaviour by apple is restored
12absolute mode active
24scrolling invertedscroll the document instead of the window...
38keep movingmove the pointer further when the finger reaches an edge of the pad...
416debuglots of output into the /var/log/system.log...
532left scrolling hotspotput the scroll sensitive area to the left...

You can turn the touchpad into an absolute pointing device by issuing sysctl -w scrollpad.activate=2. The other flags won't work then. And please don't ask me about InkWell. I have no idea how to activate it and i'm sure, it won't be working well because the finger-on-touchpad setup is way too inaccurate. And a Palm-style stylus won't be reckognized by this kind of pad anyway. I just put the absolute mode here because it was so easily available. I don't think, it has any real use.


Details:

My implementation of the touch gestures is still - well - less than perfect... In order to reckognize, that a click is not followed by a second half-tap that initiates a drag gesture, i would have to get the driver invoked when nothing happens. And that does not happen - at least not out of the box. There may be possibilities with timers and such. But i have no idea, how.

Here is a diagram of what happens / should happen when the finger taps once, one-and-a-half times or twice:

  1. this is the point, where we know for sure, that no second click or half-click will follow within the timeout limit. But if nothing follows, the driver code won't get called again...
  2. here we know for sure, that a second finger down happened - but we still don't know, if it's going to be a drag or a doubleclick...
  3. here we know for sure, that the first click really was a single event - but the second one could still start a new tap-and-a-half...
As you can see: the main discrepancy is, that in front of a drag gesture, my implementation sends an additional click. That is plain wrong and you can not drag selections that change on click this way (text selections e.g.). But it simply was the easiest to implement. And you can still resolve to using the physical button if you need to.


Download:

The ScrollPad hack is based on apples ADBMouse driver code and as such it is available under the ASPL: Apples open source license.


up Author: Heiko Hellweg,
last modified: September 2003