Skip to content

Instantly share code, notes, and snippets.

@mishurov
Last active September 2, 2019 20:14
Show Gist options
  • Save mishurov/8983f3f313b6297bda1ef32f0a21f8d2 to your computer and use it in GitHub Desktop.
Save mishurov/8983f3f313b6297bda1ef32f0a21f8d2 to your computer and use it in GitHub Desktop.
Remove the delay in macOS' touch and drag mode. It still doesn't fire events during the delay yet it removes the sticky effect.
// clang -o touchAndDragDelay touchAndDragDelay.c -framework ApplicationServices -F/System/Library/PrivateFrameworks -framework MultitouchSupport
/*
~/Library/LaunchAgents/uk.co.mishurov.touchndrag.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>uk.co.mishurov.touchndrag</string>
<key>Program</key>
<string>/Users/path/to/touchAndDragDelay</string>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
launchctl load uk.co.mishurov.touchndrag.plist
*/
#include <ApplicationServices/ApplicationServices.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;
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);
enum Mode {
Normal,
Dragged,
Released
};
static enum Mode mode = Normal;
int callbackMultitouch(int device, Finger *data, int nFingers,
double timestamp, int frame)
{
for (int i = 0; i < nFingers; i++) {
Finger *f = &data[i];
// only first contact is of interest
// state == 4 is a contact press
// state == 7 is a contact release (on ELAN1200)
if (mode != Dragged || i > 0 || f->state != 7)
return 0;
mode = Released;
#ifdef DEBUG
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);
#endif
}
#ifdef DEBUG
printf("\n");
#endif
return 0;
}
static CGPoint location = {0};
static CGEventSourceRef source;
void postMouseEvent(CGEventTapProxy proxy, CGEventType eventType) {
CGEventRef event = CGEventCreateMouseEvent(source, eventType,
location, kCGMouseButtonLeft);
CGEventTapPostEvent(proxy, event);
CFRelease(event);
}
CGEventRef callbackQuartz(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *refcon)
{
if (type == kCGEventLeftMouseDragged) {
location = CGEventGetLocation(event);
if (mode == Released)
postMouseEvent(proxy, kCGEventLeftMouseUp);
else if (mode == Normal)
mode = Dragged;
} else if (type == kCGEventLeftMouseUp && mode != Normal) {
mode = Normal;
}
return event;
}
int main(int argc, char **argv)
{
CFMachPortRef eventTap = NULL;
CGEventMask eventMask = 0;
CFRunLoopSourceRef runLoopSource = NULL;
source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
eventMask = CGEventMaskBit(kCGEventLeftMouseDown) |
CGEventMaskBit(kCGEventLeftMouseUp) |
CGEventMaskBit(kCGEventRightMouseDown) |
CGEventMaskBit(kCGEventRightMouseUp) |
CGEventMaskBit(kCGEventLeftMouseDragged) |
CGEventMaskBit(kCGEventRightMouseDragged) |
CGEventMaskBit(kCGEventScrollWheel);
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
0, eventMask, callbackQuartz, NULL);
if (NULL == eventTap) {
printf("ERROR: failed to create event tap\n");
goto fail;
}
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
MTDeviceRef dev = MTDeviceCreateDefault();
MTRegisterContactFrameCallback(dev, callbackMultitouch);
MTDeviceStart(dev, 0);
CFRunLoopRun();
fail:
if (NULL != eventTap) {
CFRelease(eventTap);
eventTap = NULL;
}
if (NULL != runLoopSource) {
CFRelease(runLoopSource);
runLoopSource = NULL;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment