devdraw: add multitouch code from Paul Lalonde

Various tweaks to avoid breaking standard mice,
but probably needs tweaks to work with multitouch
mice again.  Still, it's a start.

R=rsc
CC=plalonde, r
http://codereview.appspot.com/181124
This commit is contained in:
Russ Cox 2010-01-04 10:23:35 -08:00
parent b968422f51
commit 100ec44e51
3 changed files with 267 additions and 11 deletions

View file

@ -47,7 +47,11 @@ if [ $WSYSTYPE = x11 ]; then
XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'`
echo 'WSYSOFILES=$WSYSOFILES '$XO
elif [ $WSYSTYPE = osx ]; then
echo 'WSYSOFILES=$WSYSOFILES osx-screen.o osx-draw.o osx-srv.o'
if [ -d /System/Library/PrivateFrameworks/MultitouchSupport.framework ]; then
echo 'CFLAGS=$CFLAGS -DMULTITOUCH'
echo 'LDFLAGS=$LDFLAGS -F/System/Library/PrivateFrameworks'
fi
echo 'WSYSOFILES=$WSYSOFILES osx-screen-carbon-objc.o osx-draw.o osx-srv.o'
elif [ $WSYSTYPE = nowsys ]; then
echo 'WSYSOFILES=nowsys.o'
fi

View file

@ -22,6 +22,11 @@
#include "glendapng.h"
AUTOFRAMEWORK(Carbon)
AUTOFRAMEWORK(Cocoa)
#ifdef MULTITOUCH
AUTOFRAMEWORK(MultiTouchSupport)
#endif
#define panic sysfatal
@ -54,8 +59,190 @@ struct {
int active;
int infullscreen;
int kalting; // last keystroke was Kalt
int touched; // last mouse event was touchCallback
NSMutableArray* devicelist;
} osx;
/*
These structs are required, in order to handle some parameters returned from the
Support.framework
*/
typedef struct {
float x;
float y;
}mtPoint;
typedef struct {
mtPoint position;
mtPoint velocity;
}mtReadout;
/*
Some reversed engineered informations from MultiTouchSupport.framework
*/
typedef struct
{
int frame; //the current frame
double timestamp; //event timestamp
int identifier; //identifier guaranteed unique for life of touch per device
int state; //the current state (not sure what the values mean)
int unknown1; //no idea what this does
int unknown2; //no idea what this does either
mtReadout normalized; //the normalized position and vector of the touch (0,0 to 1,1)
float size; //the size of the touch (the area of your finger being tracked)
int unknown3; //no idea what this does
float angle; //the angle of the touch -|
float majorAxis; //the major axis of the touch -|-- an ellipsoid. you can track the angle of each finger!
float minorAxis; //the minor axis of the touch -|
mtReadout unknown4; //not sure what this is for
int unknown5[2]; //no clue
float unknown6; //no clue
}Touch;
//a reference pointer for the multitouch device
typedef void *MTDeviceRef;
//the prototype for the callback function
typedef int (*MTContactCallbackFunction)(int,Touch*,int,double,int);
//returns a pointer to the default device (the trackpad?)
MTDeviceRef MTDeviceCreateDefault(void);
//returns a CFMutableArrayRef array of all multitouch devices
CFMutableArrayRef MTDeviceCreateList(void);
//registers a device's frame callback to your callback function
void MTRegisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction);
//start sending events
void MTDeviceStart(MTDeviceRef, int);
void MTDeviceStop(MTDeviceRef);
#define kNTracks 10
struct TouchTrack {
int id;
float firstThreshTime;
mtPoint pos;
} tracks[kNTracks];
#define kSizeSensitivity 1.25f
#define kTimeSensitivity 0.03f /* seconds */
#define kButtonLimit 0.6f /* percentage from base of pad */
int
findTrack(int id)
{
int i;
for(i = 0; i < kNTracks; ++i)
if(tracks[i].id == id)
return i;
return -1;
}
#define kMoveSensitivity 0.05f
int
moved(mtPoint a, mtPoint b)
{
if(fabs(a.x - b.x) > kMoveSensitivity)
return 1;
if(fabs(a.y - b.y) > kMoveSensitivity)
return 1;
return 0;
}
int
classifyTouch(Touch *t)
{
mtPoint p;
int i;
p = t->normalized.position;
i = findTrack(t->identifier);
if(i == -1) {
i = findTrack(-1);
if(i == -1)
return 0; // No empty tracks.
tracks[i].id = t->identifier;
tracks[i].firstThreshTime = t->timestamp;
tracks[i].pos = p;
// we don't have a touch yet - we wait kTimeSensitivity before reporting it.
return 0;
}
if(t->size == 0) { // lost touch
tracks[i].id = -1;
return 0;
}
if(t->size < kSizeSensitivity) {
tracks[i].firstThreshTime = t->timestamp;
}
if((t->timestamp - tracks[i].firstThreshTime) < kTimeSensitivity) {
return 0;
}
if(p.y > kButtonLimit && t->size > kSizeSensitivity ) {
if(p.x < 0.35)
return 1;
if(p.x > 0.65)
return 4;
if(p.x > 0.35 && p.x < 0.65)
return 2;
}
return 0;
}
static ulong msec(void);
int
touchCallback(int device, Touch *data, int nFingers, double timestamp, int frame)
{
#ifdef MULTITOUCH
int buttons, delta, i;
static int obuttons;
CGPoint p;
CGEventRef e;
osx.touched = 1;
buttons = 0;
for(i = 0; i < nFingers; ++i)
buttons |= classifyTouch(data+i);
delta = buttons ^ obuttons;
obuttons = buttons;
p.x = osx.xy.x+osx.screenr.min.x;
p.y = osx.xy.y+osx.screenr.min.y;
if(delta & 1) {
e = CGEventCreateMouseEvent(NULL,
(buttons & 1) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
p,
29);
CGEventPost(kCGSessionEventTap, e);
CFRelease(e);
}
if(delta & 2) {
e = CGEventCreateMouseEvent(NULL,
(buttons & 2) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
p,
30);
CGEventPost(kCGSessionEventTap, e);
CFRelease(e);
}
if(delta & 4){
e = CGEventCreateMouseEvent(NULL,
(buttons & 4) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp,
p,
31);
CGEventPost(kCGSessionEventTap, e);
CFRelease(e);
}
return delta != 0;
#else
return 0;
#endif
}
extern int multitouch;
enum
{
WindowAttrs =
@ -89,6 +276,29 @@ enum
void screeninit(void);
void _flushmemscreen(Rectangle r);
static void
InitMultiTouch(void)
{
#ifdef MULTITOUCH
int i;
/*
* Setup multitouch queues
*/
if(!multitouch)
return;
for(i = 0; i<kNTracks; ++i)
tracks[i].id = -1;
osx.devicelist = (NSMutableArray*)MTDeviceCreateList(); //grab our device list
for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices
MTRegisterContactFrameCallback([osx.devicelist objectAtIndex:i], touchCallback); //assign callback for device
}
#endif
}
Memimage*
attachscreen(char *label, char *winsize)
{
@ -104,6 +314,8 @@ attachscreen(char *label, char *winsize)
return osx.screenimage;
}
extern int multitouch;
void
_screeninit(void)
{
@ -202,6 +414,9 @@ _screeninit(void)
ShowMenuBar();
eresized(0);
SelectWindow(osx.window);
if(multitouch)
InitMultiTouch();
InitCursor();
}
@ -256,7 +471,7 @@ eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
switch(GetEventClass(event)){
case 'P9PE':
if (GetEventKind(event) == P9PEventLabelUpdate) {
if(GetEventKind(event) == P9PEventLabelUpdate) {
qlock(&osx.labellock);
setlabel(osx.label);
qunlock(&osx.labellock);
@ -341,10 +556,18 @@ mouseevent(EventRef event)
SInt32 delta;
GetEventParameter(event, kEventParamMouseWheelDelta,
typeSInt32, 0, sizeof delta, 0, &delta);
if(delta > 0)
wheel = 8;
else
wheel = 16;
// if I have any active touches in my region, I need to ignore the wheel motion.
//int i;
//for(i = 0; i < kNTracks; ++i) {
// if(tracks[i].id != -1 && tracks[i].pos.y > kButtonLimit) break;
//}
//if(i == kNTracks) { // No active touches, go ahead and scroll.
if(delta > 0)
wheel = 8;
else
wheel = 16;
//}
break;
case kEventMouseDown:
@ -355,11 +578,20 @@ mouseevent(EventRef event)
GetEventParameter(event, kEventParamKeyModifiers,
typeUInt32, 0, sizeof mod, 0, &mod);
// OS X swaps button 2 and 3
but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
if(osx.touched) {
// in multitouch we use the clicks down to enable our
// virtual buttons.
if(but & 0x3)
but = but >> 29;
else
but = 0;
osx.touched = 0;
} else {
// OS X swaps button 2 and 3
but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
but = mouseswap(but);
}
but = mouseswap(but);
// Apply keyboard modifiers and pretend it was a real mouse button.
// (Modifiers typed while holding the button go into kbuttons,
// but this one does not.)
@ -487,7 +719,7 @@ kbdevent(EventRef event)
k = keycvt[code];
if(k == 0)
return noErr;
else if(k > 0)
if(k > 0)
keystroke(k);
else{
UniChar uc;
@ -625,6 +857,21 @@ _flushmemscreen(Rectangle r)
void
activated(int active)
{
#ifdef MULTITOUCH
int i;
if(active) {
for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices
MTDeviceStart([osx.devicelist objectAtIndex:i], 0); //start sending events
}
} else {
for(i = 0; i<[osx.devicelist count]; i++) { //iterate available devices
MTDeviceStop([osx.devicelist objectAtIndex:i]); //stop sending events
}
for(i = 0; i<kNTracks; ++i) {
tracks[i].id = -1;
}
}
#endif
osx.active = active;
}
@ -676,6 +923,7 @@ setmouse(Point p)
cgp.x = p.x + osx.screenr.min.x;
cgp.y = p.y + osx.screenr.min.y;
CGWarpMouseCursorPosition(cgp);
osx.xy = p;
}
void

View file

@ -89,6 +89,7 @@ zunlock(void)
int chatty;
int drawsleep;
int trace;
int multitouch = 1;
void
usage(void)
@ -130,6 +131,9 @@ threadmain(int argc, char **argv)
case 'D':
chatty++;
break;
case 'M':
multitouch = 0;
break;
default:
usage();
}ARGEND