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:
parent
b968422f51
commit
100ec44e51
3 changed files with 267 additions and 11 deletions
|
|
@ -47,7 +47,11 @@ if [ $WSYSTYPE = x11 ]; then
|
||||||
XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'`
|
XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'`
|
||||||
echo 'WSYSOFILES=$WSYSOFILES '$XO
|
echo 'WSYSOFILES=$WSYSOFILES '$XO
|
||||||
elif [ $WSYSTYPE = osx ]; then
|
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
|
elif [ $WSYSTYPE = nowsys ]; then
|
||||||
echo 'WSYSOFILES=nowsys.o'
|
echo 'WSYSOFILES=nowsys.o'
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@
|
||||||
#include "glendapng.h"
|
#include "glendapng.h"
|
||||||
|
|
||||||
AUTOFRAMEWORK(Carbon)
|
AUTOFRAMEWORK(Carbon)
|
||||||
|
AUTOFRAMEWORK(Cocoa)
|
||||||
|
|
||||||
|
#ifdef MULTITOUCH
|
||||||
|
AUTOFRAMEWORK(MultiTouchSupport)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define panic sysfatal
|
#define panic sysfatal
|
||||||
|
|
||||||
|
|
@ -54,8 +59,190 @@ struct {
|
||||||
int active;
|
int active;
|
||||||
int infullscreen;
|
int infullscreen;
|
||||||
int kalting; // last keystroke was Kalt
|
int kalting; // last keystroke was Kalt
|
||||||
|
int touched; // last mouse event was touchCallback
|
||||||
|
NSMutableArray* devicelist;
|
||||||
} osx;
|
} 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
|
enum
|
||||||
{
|
{
|
||||||
WindowAttrs =
|
WindowAttrs =
|
||||||
|
|
@ -89,6 +276,29 @@ enum
|
||||||
void screeninit(void);
|
void screeninit(void);
|
||||||
void _flushmemscreen(Rectangle r);
|
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*
|
Memimage*
|
||||||
attachscreen(char *label, char *winsize)
|
attachscreen(char *label, char *winsize)
|
||||||
{
|
{
|
||||||
|
|
@ -104,6 +314,8 @@ attachscreen(char *label, char *winsize)
|
||||||
return osx.screenimage;
|
return osx.screenimage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int multitouch;
|
||||||
|
|
||||||
void
|
void
|
||||||
_screeninit(void)
|
_screeninit(void)
|
||||||
{
|
{
|
||||||
|
|
@ -202,6 +414,9 @@ _screeninit(void)
|
||||||
ShowMenuBar();
|
ShowMenuBar();
|
||||||
eresized(0);
|
eresized(0);
|
||||||
SelectWindow(osx.window);
|
SelectWindow(osx.window);
|
||||||
|
|
||||||
|
if(multitouch)
|
||||||
|
InitMultiTouch();
|
||||||
|
|
||||||
InitCursor();
|
InitCursor();
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +471,7 @@ eventhandler(EventHandlerCallRef next, EventRef event, void *arg)
|
||||||
switch(GetEventClass(event)){
|
switch(GetEventClass(event)){
|
||||||
|
|
||||||
case 'P9PE':
|
case 'P9PE':
|
||||||
if (GetEventKind(event) == P9PEventLabelUpdate) {
|
if(GetEventKind(event) == P9PEventLabelUpdate) {
|
||||||
qlock(&osx.labellock);
|
qlock(&osx.labellock);
|
||||||
setlabel(osx.label);
|
setlabel(osx.label);
|
||||||
qunlock(&osx.labellock);
|
qunlock(&osx.labellock);
|
||||||
|
|
@ -341,10 +556,18 @@ mouseevent(EventRef event)
|
||||||
SInt32 delta;
|
SInt32 delta;
|
||||||
GetEventParameter(event, kEventParamMouseWheelDelta,
|
GetEventParameter(event, kEventParamMouseWheelDelta,
|
||||||
typeSInt32, 0, sizeof delta, 0, &delta);
|
typeSInt32, 0, sizeof delta, 0, &delta);
|
||||||
if(delta > 0)
|
|
||||||
wheel = 8;
|
// if I have any active touches in my region, I need to ignore the wheel motion.
|
||||||
else
|
//int i;
|
||||||
wheel = 16;
|
//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;
|
break;
|
||||||
|
|
||||||
case kEventMouseDown:
|
case kEventMouseDown:
|
||||||
|
|
@ -355,11 +578,20 @@ mouseevent(EventRef event)
|
||||||
GetEventParameter(event, kEventParamKeyModifiers,
|
GetEventParameter(event, kEventParamKeyModifiers,
|
||||||
typeUInt32, 0, sizeof mod, 0, &mod);
|
typeUInt32, 0, sizeof mod, 0, &mod);
|
||||||
|
|
||||||
// OS X swaps button 2 and 3
|
if(osx.touched) {
|
||||||
but = (but & ~6) | ((but & 4)>>1) | ((but&2)<<1);
|
// 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.
|
// Apply keyboard modifiers and pretend it was a real mouse button.
|
||||||
// (Modifiers typed while holding the button go into kbuttons,
|
// (Modifiers typed while holding the button go into kbuttons,
|
||||||
// but this one does not.)
|
// but this one does not.)
|
||||||
|
|
@ -487,7 +719,7 @@ kbdevent(EventRef event)
|
||||||
k = keycvt[code];
|
k = keycvt[code];
|
||||||
if(k == 0)
|
if(k == 0)
|
||||||
return noErr;
|
return noErr;
|
||||||
else if(k > 0)
|
if(k > 0)
|
||||||
keystroke(k);
|
keystroke(k);
|
||||||
else{
|
else{
|
||||||
UniChar uc;
|
UniChar uc;
|
||||||
|
|
@ -625,6 +857,21 @@ _flushmemscreen(Rectangle r)
|
||||||
void
|
void
|
||||||
activated(int active)
|
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;
|
osx.active = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -676,6 +923,7 @@ setmouse(Point p)
|
||||||
cgp.x = p.x + osx.screenr.min.x;
|
cgp.x = p.x + osx.screenr.min.x;
|
||||||
cgp.y = p.y + osx.screenr.min.y;
|
cgp.y = p.y + osx.screenr.min.y;
|
||||||
CGWarpMouseCursorPosition(cgp);
|
CGWarpMouseCursorPosition(cgp);
|
||||||
|
osx.xy = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -89,6 +89,7 @@ zunlock(void)
|
||||||
int chatty;
|
int chatty;
|
||||||
int drawsleep;
|
int drawsleep;
|
||||||
int trace;
|
int trace;
|
||||||
|
int multitouch = 1;
|
||||||
|
|
||||||
void
|
void
|
||||||
usage(void)
|
usage(void)
|
||||||
|
|
@ -130,6 +131,9 @@ threadmain(int argc, char **argv)
|
||||||
case 'D':
|
case 'D':
|
||||||
chatty++;
|
chatty++;
|
||||||
break;
|
break;
|
||||||
|
case 'M':
|
||||||
|
multitouch = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
}ARGEND
|
}ARGEND
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue