Last active
September 2, 2019 20:14
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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