Skip to content

Instantly share code, notes, and snippets.

@rmhsilva
Last active December 13, 2025 02:09
Show Gist options
  • Select an option

  • Save rmhsilva/61cc45587ed34707da34818a76476e11 to your computer and use it in GitHub Desktop.

Select an option

Save rmhsilva/61cc45587ed34707da34818a76476e11 to your computer and use it in GitHub Desktop.
Accessing raw multitouch trackpad data (MacOS)

A collection of information about accessing raw MultiTouch events on MacOS.

Compiled while building mtif (a MultiTouch interface for common lisp).

Useful URLS

Link with g++:

You need the MultitouchSupport framework:

LIBS=-F/System/Library/PrivateFrameworks -framework MultitouchSupport
g++ $(LIBS) ...

Reading the trackpad

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!

Racket Code

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)

Full Headers

/*
 *  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);
*/
@mate-h
Copy link
Copy Markdown

mate-h commented May 12, 2020

Here is a CMake snippet for those interested:

# Link MultitouchSupport Apple framework
find_library( LIBS MultitouchSupport /System/Library/PrivateFrameworks )
message(STATUS "Found library: ${LIBS}" )
target_link_libraries( ${library_name}
    ${LIBS}
)

@rmhsilva
Copy link
Copy Markdown
Author

Thanks @mate-h!

@CuberL
Copy link
Copy Markdown

CuberL commented Sep 23, 2024

If anyone got undefined symbol when compile the example code in C++, like:

ld: Undefined symbols:
  MTDeviceStart(void*, int), referenced from:
      _main in main-60e98d.o
  MTDeviceCreateDefault(), referenced from:
      _main in main-60e98d.o
  MTRegisterContactFrameCallback(void*, int (*)(int, Finger*, int, double, int)), referenced from:
      _main in main-60e98d.o

then can try to wrap extern "C" to include your header file

extern "C" {
#include "MultitouchSupport.h"
}
...

And the problem should be solved.

@KSroido
Copy link
Copy Markdown

KSroido commented May 19, 2025

very helpful and useful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment