A collection of information about accessing raw MultiTouch events on MacOS.
- http://www.makeitgo.ws/articles/fingermgmt/
- https://github.com/fajran/tongseng/blob/master/Makefile
- https://github.com/calftrail/TrackMagic/blob/master/MultitouchSupport.h
- https://encyclopediaofdaniel.com/blog/making-the-magic-trackpad-work/
- https://github.com/calftrail/Touch/blob/master/TouchSynthesis/MultitouchSupport.h
You need the MultitouchSupport framework:
LIBS=-F/System/Library/PrivateFrameworks -framework MultitouchSupport
g++ $(LIBS) ...
Original code from https://web.archive.org/web/20151012175118/http://steike.com/code/multitouch/:
#include <math.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
typedef struct { float x,y; } mtPoint;
typedef struct { mtPoint pos,vel; } mtReadout;
typedef struct {
int frame;
double timestamp;
int identifier, state, foo3, foo4;
mtReadout normalized;
float size;
int zero1;
float angle, majorAxis, minorAxis; // ellipsoid
mtReadout mm;
int zero2[2];
float unk2;
} Finger;
typedef void *MTDeviceRef;
typedef int (*MTContactCallbackFunction)(int,Finger*,int,double,int);
MTDeviceRef MTDeviceCreateDefault();
void MTRegisterContactFrameCallback(MTDeviceRef, MTContactCallbackFunction);
void MTDeviceStart(MTDeviceRef, int); // thanks comex
int callback(int device, Finger *data, int nFingers, double timestamp, int frame) {
for (int i=0; i<nFingers; i++) {
Finger *f = &data[i];
printf("Frame %7d: Angle %6.2f, ellipse %6.3f x%6.3f; "
"position (%6.3f,%6.3f) vel (%6.3f,%6.3f) "
"ID %d, state %d [%d %d?] size %6.3f, %6.3f?\n",
f->frame,
f->angle * 90 / atan2(1,0),
f->majorAxis,
f->minorAxis,
f->normalized.pos.x,
f->normalized.pos.y,
f->normalized.vel.x,
f->normalized.vel.y,
f->identifier, f->state, f->foo3, f->foo4,
f->size, f->unk2);
}
printf("\n");
return 0;
}
int main() {
MTDeviceRef dev = MTDeviceCreateDefault();
MTRegisterContactFrameCallback(dev, callback);
MTDeviceStart(dev, 0);
printf("Ctrl-C to abort\n");
sleep(-1);
return 0;
}
His notes:
identifier
Persistent identifier for each touch -- each "finger" may move around the Fingers[] array, but this will remain the same.
normalized.pos.x
Current position, from [0..1]
size
Close to zero if you're barely touching the touch pad
angle, majorAxis, minorAxis
Describes the ellipsoid of your finger. Yes, you can track rotation of a single finger!
From https://gist.github.com/dchest/718922
; Ported from http://pb.lericson.se/p/FpbYhX/
(require ffi/unsafe
ffi/unsafe/atomic)
(define libmulti (ffi-lib "/System/Library/PrivateFrameworks/MultitouchSupport.framework/MultitouchSupport"))
(define CFArrayRef _pointer)
(define CFMutableArrayRef _pointer)
(define CFIndex _long)
(define CFArrayGetCount
(get-ffi-obj "CFArrayGetCount" libmulti
(_fun CFArrayRef -> CFIndex)))
(define CFArrayGetValueAtIndex
(get-ffi-obj "CFArrayGetValueAtIndex" libmulti
(_fun CFArrayRef CFIndex -> _pointer)))
(define MTDeviceCreateList
(get-ffi-obj "MTDeviceCreateList" libmulti
(_fun -> CFMutableArrayRef)))
(define-cstruct _MTPoint ([x _float] [y _float]))
(define-cstruct _MTVector ([position _MTPoint] [velocity _MTPoint]))
(define-cstruct _MTData ([frame _int]
[timestamp _double]
[identifier _int]
[state _int]
[unknown1 _int]
[unknown2 _int]
[normalized _MTVector]
[size _float]
[unknown3 _int]
[angle _float]
[major_axis _float]
[minor_axis _float]
[unknown4 _MTVector]
[unknown5_1 _int]
[unknown5_2 _int]
[unknown6 _float]))
(define MTDataRef _MTData-pointer)
;; A queue that implements locking by atomic actions,
;; since an async-apply function cannot block on a lock.
(define sema (make-semaphore))
(define queue null)
(define (enqueue thunk)
(set! queue (append queue (list thunk)))
(semaphore-post sema))
(define (dequeue)
(semaphore-wait sema)
(start-atomic)
(let ([v (car queue)])
(set! queue (cdr queue))
(end-atomic)
v))
(define MTContactCallbackFunction
(_fun #:async-apply enqueue _int MTDataRef _int _double _int -> _int))
(define MTDeviceRef _pointer)
(define MTRegisterContactFrameCallback
(get-ffi-obj "MTRegisterContactFrameCallback" libmulti
(_fun MTDeviceRef MTContactCallbackFunction -> _void)))
(define MTDeviceStart
(get-ffi-obj "MTDeviceStart" libmulti
(_fun MTDeviceRef _int -> _void)))
(define (multitouch-register-callback proc)
(let ([devices (MTDeviceCreateList)])
(for ([i (in-range (CFArrayGetCount devices))])
(let ([device (CFArrayGetValueAtIndex devices i)])
(MTRegisterContactFrameCallback device proc)
(MTDeviceStart device 0)))))
(multitouch-register-callback
(lambda (device data-ptr n-fingers timestamp frame)
(for ([i (in-range n-fingers)])
(let* ([data (ptr-ref data-ptr _MTData i)]
[vector (MTData-normalized data)]
[position (MTVector-position vector)]
[x (* 100 (MTPoint-x position))]
[y (* 100 (MTPoint-y position))]
[size (* 30 (MTData-size data))])
(draw-circle (send canvas get-dc) x y size)))
;(printf "d=~a x=~a, y=~a, size=~a\n" i x y size)))
0))
(displayln "Running")
;; Thread to run async calls in the background:
(thread (lambda ()
(let loop ()
(let ([thunk (dequeue)])
(thunk)
(loop)))))
; Demo
(define FRAMEWIDTH 800)
(define FRAMEHIGHT 600)
(define frame (new frame% [label "Multitouch Example"]
[width FRAMEWIDTH]
[height FRAMEHIGHT]))
(define canvas (new canvas% [parent frame]))
(define yellow-brush (make-object brush% "YELLOW" 'solid))
(define blue-pen (make-object pen% "BLUE" 1 'solid))
(define (convert-x x)
(/ (* x FRAMEWIDTH) 100))
(define (convert-y y)
(- FRAMEHIGHT (/ (* y FRAMEHIGHT) 100)))
(define (draw-circle dc x y size)
(send dc set-pen blue-pen)
(send dc set-brush yellow-brush)
(send dc draw-ellipse (convert-x x) (convert-y y) size size))
(send frame show #t)
(send frame center)
(send frame set-cursor (make-object cursor% 'blank))
; Wait a second to let the window get ready
(sleep/yield 1)
/*
* MultitouchSupport.h
* TouchSynthesis
*
* Created by Nathan Vander Wilt on 1/13/10.
* Copyright 2010 Calf Trail Software, LLC. All rights reserved.
*
*/
typedef struct {
float x;
float y;
} MTPoint;
typedef struct {
MTPoint position;
MTPoint velocity;
} MTVector;
enum {
MTTouchStateNotTracking = 0,
MTTouchStateStartInRange = 1,
MTTouchStateHoverInRange = 2,
MTTouchStateMakeTouch = 3,
MTTouchStateTouching = 4,
MTTouchStateBreakTouch = 5,
MTTouchStateLingerInRange = 6,
MTTouchStateOutOfRange = 7
};
typedef uint32_t MTTouchState;
typedef struct {
int32_t frame;
double timestamp;
int32_t pathIndex; // "P" (~transducerIndex)
MTTouchState state;
int32_t fingerID; // "F" (~identity)
int32_t handID; // "H" (always 1)
MTVector normalizedVector;
float zTotal; // "ZTot" (~quality, multiple of 1/8 between 0 and 1)
int32_t field9; // always 0
float angle;
float majorAxis;
float minorAxis;
MTVector absoluteVector; // "mm"
int32_t field14; // always 0
int32_t field15; // always 0
float zDensity; // "ZDen" (~density)
} MTTouch;
//typedef const void* MTDeviceRef;
typedef CFTypeRef MTDeviceRef;
double MTAbsoluteTimeGetCurrent();
bool MTDeviceIsAvailable(); // true if can create default device
CFArrayRef MTDeviceCreateList(); // creates for driver types 0, 1, 4, 2, 3
MTDeviceRef MTDeviceCreateDefault();
MTDeviceRef MTDeviceCreateFromDeviceID(int64_t);
MTDeviceRef MTDeviceCreateFromService(io_service_t);
MTDeviceRef MTDeviceCreateFromGUID(uuid_t); // GUID's compared by pointer, not value!
void MTDeviceRelease(MTDeviceRef);
CFRunLoopSourceRef MTDeviceCreateMultitouchRunLoopSource(MTDeviceRef);
OSStatus MTDeviceScheduleOnRunLoop(MTDeviceRef, CFRunLoopRef, CFStringRef);
OSStatus MTDeviceStart(MTDeviceRef, int);
OSStatus MTDeviceStop(MTDeviceRef);
bool MTDeviceIsRunning(MTDeviceRef);
bool MTDeviceIsValid(MTDeviceRef);
bool MTDeviceIsBuiltIn(MTDeviceRef) __attribute__ ((weak_import)); // no 10.5
bool MTDeviceIsOpaqueSurface(MTDeviceRef);
io_service_t MTDeviceGetService(MTDeviceRef);
OSStatus MTDeviceGetSensorSurfaceDimensions(MTDeviceRef, int*, int*);
OSStatus MTDeviceGetFamilyID(MTDeviceRef, int*);
OSStatus MTDeviceGetDeviceID(MTDeviceRef, uint64_t*) __attribute__ ((weak_import)); // no 10.5
OSStatus MTDeviceGetDriverType(MTDeviceRef, int*);
OSStatus MTDeviceGetActualType(MTDeviceRef, int*);
OSStatus MTDeviceGetGUID(MTDeviceRef, uuid_t*);
typedef void (*MTFrameCallbackFunction)(MTDeviceRef device,
MTTouch touches[], size_t numTouches,
double timestamp, size_t frame);
void MTRegisterContactFrameCallback(MTDeviceRef, MTFrameCallbackFunction);
typedef void (*MTFrameCallbackRefconFunction)(MTDeviceRef device,
MTTouch touches[], size_t numTouches,
double timestamp, size_t frame, void* refcon);
void MTRegisterContactFrameCallbackWithRefcon(MTDeviceRef, MTFrameCallbackRefconFunction, void* refcon);
void MTUnregisterContactFrameCallback(MTDeviceRef, MTFrameCallbackRefconFunction);
typedef void (*MTPathCallbackFunction)(MTDeviceRef device, long pathID, long state, MTTouch* touch);
MTPathCallbackFunction MTPathPrintCallback;
void MTRegisterPathCallback(MTDeviceRef, MTPathCallbackFunction);
/*
// callbacks never called (need different flags?)
typedef void (*MTImageCallbackFunction)(MTDeviceRef, void*, void*);
MTImageCallbackFunction MTImagePrintCallback;
void MTRegisterMultitouchImageCallback(MTDeviceRef, MTImageCallbackFunction);
*/
/*
// these log error
void MTVibratorRunForDuration(MTDeviceRef,long);
void MTVibratorStop(MTDeviceRef);
*/
Here is a
CMake
snippet for those interested: