aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2010-01-04 10:23:35 -0800
committerRuss Cox <rsc@swtch.com>2010-01-04 10:23:35 -0800
commit100ec44e5170878bbd7fd28f7f68d884d0618173 (patch)
treeaa68d15a8470925872e202a8e1385b9699da8a5e /src
parentb968422f51732c492ff4081786b713ace99835c1 (diff)
downloadplan9port-100ec44e5170878bbd7fd28f7f68d884d0618173.tar.gz
plan9port-100ec44e5170878bbd7fd28f7f68d884d0618173.tar.bz2
plan9port-100ec44e5170878bbd7fd28f7f68d884d0618173.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/cmd/devdraw/mkwsysrules.sh6
-rw-r--r--src/cmd/devdraw/osx-screen-carbon.m (renamed from src/cmd/devdraw/osx-screen.c)268
-rw-r--r--src/cmd/devdraw/osx-srv.c4
3 files changed, 267 insertions, 11 deletions
diff --git a/src/cmd/devdraw/mkwsysrules.sh b/src/cmd/devdraw/mkwsysrules.sh
index 88102c89..5e65a29c 100644
--- a/src/cmd/devdraw/mkwsysrules.sh
+++ b/src/cmd/devdraw/mkwsysrules.sh
@@ -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
diff --git a/src/cmd/devdraw/osx-screen.c b/src/cmd/devdraw/osx-screen-carbon.m
index f6ed3e93..20bdb4ca 100644
--- a/src/cmd/devdraw/osx-screen.c
+++ b/src/cmd/devdraw/osx-screen-carbon.m
@@ -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
diff --git a/src/cmd/devdraw/osx-srv.c b/src/cmd/devdraw/osx-srv.c
index ae14ba18..6cbb5235 100644
--- a/src/cmd/devdraw/osx-srv.c
+++ b/src/cmd/devdraw/osx-srv.c
@@ -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