From a3a1ff5c34cd6c939b1d07d2df1911868e53da9a Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Thu, 22 Feb 2024 14:50:02 -0800 Subject: [PATCH 1/7] Only paste on middle button press ie, ignore side buttons like forward/back --- .gitignore | 1 + macpaste.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bbf313b..6b6c16d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.ko *.obj *.elf +macpaste # Precompiled Headers *.gch diff --git a/macpaste.c b/macpaste.c index 9416330..18e530d 100644 --- a/macpaste.c +++ b/macpaste.c @@ -94,11 +94,13 @@ static void paste(CGEventRef event) { void * refcon ) { int* dontpaste = refcon; + int button; switch ( type ) { case kCGEventOtherMouseDown: - if (*dontpaste == 0) - paste( event ); + button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); + if (*dontpaste == 0 && button == 2) + paste( event ); break; case kCGEventLeftMouseDown: From b4cc82d4c6a47f0c792e54d86fac1a716c44746d Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Thu, 22 Feb 2024 19:02:59 -0800 Subject: [PATCH 2/7] Only paste when the cursor is an ibeam Don't paste unless the cursor is an ibeam, aka is hovering over a text field. Should let the middle button work for eg browser links in new tab hopefully --- Makefile | 4 ++-- macpaste.c => macpaste.m | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) rename macpaste.c => macpaste.m (94%) diff --git a/Makefile b/Makefile index bf6c23a..8ad943e 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,8 @@ help: clean: -rm -f macpaste -macpaste: macpaste.c - gcc -O2 -framework ApplicationServices -o macpaste macpaste.c +macpaste: macpaste.m + gcc -O2 -framework AppKit -framework ApplicationServices -o macpaste macpaste.m run: ./macpaste diff --git a/macpaste.c b/macpaste.m similarity index 94% rename from macpaste.c rename to macpaste.m index 18e530d..e47bfda 100644 --- a/macpaste.c +++ b/macpaste.m @@ -17,6 +17,7 @@ #include #include // kVK_ANSI_* #include // gettimeofday +#include char isDragging = 0; long long prevClickTime = 0; @@ -25,7 +26,7 @@ long long curClickTime = 0; CGEventTapLocation tapA = kCGAnnotatedSessionEventTap; CGEventTapLocation tapH = kCGHIDEventTap; -#define DOUBLE_CLICK_MILLIS 500 +#define DOUBLE_CLICK_MILLIS 200 long long now() { struct timeval te; @@ -99,7 +100,9 @@ static void paste(CGEventRef event) { { case kCGEventOtherMouseDown: button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); - if (*dontpaste == 0 && button == 2) + NSCursor* cursor = [NSCursor currentSystemCursor]; + NSCursor* ibeam = [NSCursor IBeamCursor]; + if (*dontpaste == 0 && button == 2 && NSEqualPoints([cursor hotSpot] , [ibeam hotSpot] )) paste( event ); break; @@ -134,6 +137,7 @@ static void paste(CGEventRef event) { CFMachPortRef myEventTap; CFRunLoopSourceRef eventTapRLSrc; + // parse args for -n flag int c; int dontpaste = 0; @@ -152,6 +156,7 @@ static void paste(CGEventRef event) { CGEventMaskBit( kCGEventLeftMouseDown ) | CGEventMaskBit( kCGEventLeftMouseUp ) | CGEventMaskBit( kCGEventLeftMouseDragged ); + NSApplicationLoad(); // Create the Tap myEventTap = CGEventTapCreate ( From 0c481d95e22c49ef9be5e8487b979adb4b9e0bfa Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Wed, 28 Feb 2024 14:54:49 -0800 Subject: [PATCH 3/7] Only copy on drag if we've dragged more than 5 px away Prevent spurious copys on click while moving the mouse slightly --- macpaste.m | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/macpaste.m b/macpaste.m index e47bfda..dc9888c 100644 --- a/macpaste.m +++ b/macpaste.m @@ -22,6 +22,7 @@ char isDragging = 0; long long prevClickTime = 0; long long curClickTime = 0; +NSPoint initialLocation; CGEventTapLocation tapA = kCGAnnotatedSessionEventTap; CGEventTapLocation tapH = kCGHIDEventTap; @@ -102,22 +103,38 @@ static CGEventRef mouseCallback ( button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); NSCursor* cursor = [NSCursor currentSystemCursor]; NSCursor* ibeam = [NSCursor IBeamCursor]; - if (*dontpaste == 0 && button == 2 && NSEqualPoints([cursor hotSpot] , [ibeam hotSpot] )) + if (*dontpaste == 0 && button == 2 && NSEqualPoints([cursor hotSpot] , [ibeam hotSpot] )) { + // NSLog(@"paste %@", NSStringFromPoint( [NSEvent mouseLocation])); paste( event ); + } break; case kCGEventLeftMouseDown: + //NSLog(@"down %@", NSStringFromPoint( [NSEvent mouseLocation])); recordClickTime(); break; case kCGEventLeftMouseUp: - if ( isDoubleClick() || isDragging ) { + //NSLog(@"up %@", NSStringFromPoint( [NSEvent mouseLocation])); + if (isDoubleClick()) { + //NSLog(@"copydblc %@", NSStringFromPoint( [NSEvent mouseLocation])); copy(); } + if (isDragging) { + NSPoint clickLocation = [NSEvent mouseLocation]; + int xdiff = fabs(initialLocation.x-clickLocation.x); + int ydiff = fabs(initialLocation.y-clickLocation.y); + if (xdiff > 5 || ydiff > 5) { + //NSLog(@"copydrag %@ %@", NSStringFromPoint(initialLocation), NSStringFromPoint( [NSEvent mouseLocation])); + copy(); + } + } isDragging = 0; break; case kCGEventLeftMouseDragged: + if (!isDragging) + initialLocation = [NSEvent mouseLocation]; isDragging = 1; break; From 97bff2bca95f8464ceffe21327aa22988b4c359c Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Tue, 5 Mar 2024 11:53:01 -0800 Subject: [PATCH 4/7] Don't copy on double click Only copy when dragging to highlight, to make the highlight -> double click -> paste use case more useful --- macpaste.m | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/macpaste.m b/macpaste.m index dc9888c..17a681b 100644 --- a/macpaste.m +++ b/macpaste.m @@ -20,22 +20,11 @@ #include char isDragging = 0; -long long prevClickTime = 0; -long long curClickTime = 0; NSPoint initialLocation; CGEventTapLocation tapA = kCGAnnotatedSessionEventTap; CGEventTapLocation tapH = kCGHIDEventTap; -#define DOUBLE_CLICK_MILLIS 200 - -long long now() { - struct timeval te; - gettimeofday( & te, NULL ); - long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // caculate milliseconds - return milliseconds; -} - static void paste(CGEventRef event) { // Mouse click to focus and position insertion cursor. CGPoint mouseLocation = CGEventGetLocation( event ); @@ -76,19 +65,6 @@ static void copy() { CFRelease( source ); } - static void recordClickTime() { - prevClickTime = curClickTime; - curClickTime = now(); - } - - static char isDoubleClickSpeed() { - return ( curClickTime - prevClickTime ) < DOUBLE_CLICK_MILLIS; - } - - static char isDoubleClick() { - return isDoubleClickSpeed(); - } - static CGEventRef mouseCallback ( CGEventTapProxy proxy, CGEventType type, @@ -109,17 +85,8 @@ static CGEventRef mouseCallback ( } break; - case kCGEventLeftMouseDown: - //NSLog(@"down %@", NSStringFromPoint( [NSEvent mouseLocation])); - recordClickTime(); - break; - case kCGEventLeftMouseUp: //NSLog(@"up %@", NSStringFromPoint( [NSEvent mouseLocation])); - if (isDoubleClick()) { - //NSLog(@"copydblc %@", NSStringFromPoint( [NSEvent mouseLocation])); - copy(); - } if (isDragging) { NSPoint clickLocation = [NSEvent mouseLocation]; int xdiff = fabs(initialLocation.x-clickLocation.x); From 60314dd58fcd20e789f4afb7492a69f4437bf817 Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Wed, 6 Mar 2024 15:43:26 -0800 Subject: [PATCH 5/7] Copy on triple click Instead of copy on double click, do copy on triple click because that's unlikely to be followed by an immediate paste, but does highlight eg browser url bar which is usually worth a copy. --- macpaste.m | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/macpaste.m b/macpaste.m index 17a681b..9b58992 100644 --- a/macpaste.m +++ b/macpaste.m @@ -20,11 +20,23 @@ #include char isDragging = 0; +long long prevPrevClickTime = 0; +long long prevClickTime = 0; +long long curClickTime = 0; NSPoint initialLocation; CGEventTapLocation tapA = kCGAnnotatedSessionEventTap; CGEventTapLocation tapH = kCGHIDEventTap; +#define TRIPLE_CLICK_MILLIS 500 + +long long now() { + struct timeval te; + gettimeofday( & te, NULL ); + long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // caculate milliseconds + return milliseconds; +} + static void paste(CGEventRef event) { // Mouse click to focus and position insertion cursor. CGPoint mouseLocation = CGEventGetLocation( event ); @@ -65,6 +77,16 @@ static void copy() { CFRelease( source ); } + static void recordClickTime() { + prevPrevClickTime = prevClickTime; + prevClickTime = curClickTime; + curClickTime = now(); + } + + static char isTripleClick() { + return ( curClickTime - prevPrevClickTime ) < TRIPLE_CLICK_MILLIS; + } + static CGEventRef mouseCallback ( CGEventTapProxy proxy, CGEventType type, @@ -85,8 +107,17 @@ static CGEventRef mouseCallback ( } break; + case kCGEventLeftMouseDown: + //NSLog(@"down %@", NSStringFromPoint( [NSEvent mouseLocation])); + recordClickTime(); + break; + case kCGEventLeftMouseUp: //NSLog(@"up %@", NSStringFromPoint( [NSEvent mouseLocation])); + if (isTripleClick()) { + NSLog(@"copytrlc %@", NSStringFromPoint( [NSEvent mouseLocation])); + copy(); + } if (isDragging) { NSPoint clickLocation = [NSEvent mouseLocation]; int xdiff = fabs(initialLocation.x-clickLocation.x); From e724da8deae556f9eb969d688d4c2096227c09dc Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Wed, 20 Mar 2024 11:51:07 -0700 Subject: [PATCH 6/7] Manage a separate paste buffer for middle click When we copy, save the current clipboard prior to copying, then when we notice the clipboard change, store that clipboard in a separate pasteItems and restore the old clipboard. When we paste via middle button, set the clipboard to the stored pasteItems, and when we can set the clipboard back to the saved current clipboard. Because of that, restore copy-on-double-click behavior, as the typical patterns work well with a separate clipboard and selection buffer. There's still a little bit of a race if you select/paste too fast though? --- macpaste.m | 426 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 271 insertions(+), 155 deletions(-) diff --git a/macpaste.m b/macpaste.m index 9b58992..276b1c8 100644 --- a/macpaste.m +++ b/macpaste.m @@ -14,192 +14,308 @@ // // Terminate with Ctrl+C +#include +#include +#include #include #include // kVK_ANSI_* -#include // gettimeofday -#include +#include // gettimeofday char isDragging = 0; long long prevPrevClickTime = 0; long long prevClickTime = 0; long long curClickTime = 0; NSPoint initialLocation; +NSMutableArray *lastClipItems = NULL; +long lastClipCount = 0; +long lastTouchedCount = 0; +NSMutableArray *pasteItems = NULL; CGEventTapLocation tapA = kCGAnnotatedSessionEventTap; CGEventTapLocation tapH = kCGHIDEventTap; #define TRIPLE_CLICK_MILLIS 500 +#define DOUBLE_CLICK_MILLIS 200 long long now() { struct timeval te; - gettimeofday( & te, NULL ); - long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // caculate milliseconds + gettimeofday(&te, NULL); + long long milliseconds = + te.tv_sec * 1000LL + te.tv_usec / 1000; // caculate milliseconds return milliseconds; } +static NSMutableArray *copy_paste_items(NSArray *items) { + NSMutableArray *newPasteItems = [[NSMutableArray array] retain]; + for (NSPasteboardItem *item in items) { + NSPasteboardItem *dataHolder = [[NSPasteboardItem alloc] init]; + for (NSString *type in [item types]) { + NSData *data = [[item dataForType:type] mutableCopy]; + if (data) { + [dataHolder setData:data forType:type]; + } + } + [newPasteItems addObject:dataHolder]; + } + return newPasteItems; +} + +static void logInfo(NSString *location) { + // NSString *pinfo = NULL; + // NSString *linfo = NULL; + // NSString *cinfo = NULL; + // if (pasteItems != NULL && [pasteItems count]) { + // pinfo = [pasteItems[0] stringForType:NSPasteboardTypeString]; + // } + // if (lastClipItems != NULL && [lastClipItems count]) { + // linfo = [lastClipItems[0] stringForType:NSPasteboardTypeString]; + // } + // if ([[[NSPasteboard generalPasteboard] pasteboardItems] count]) { + // cinfo = [[[NSPasteboard generalPasteboard] pasteboardItems][0] + // stringForType:NSPasteboardTypeString]; + // } + // NSLog(@"%@: %ld\npaste: %@\nlastclip %@\nactual clip: %@ (%ld)", location, + // lastTouchedCount, pinfo, linfo, cinfo, [[NSPasteboard + // generalPasteboard] changeCount]); +} + static void paste(CGEventRef event) { + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + if (pasteItems != NULL) { + // prior to posting a middle click paste, put the selection buffer in the + // clipboard + // NSLog(@"WRITING %@ to clip from middle button", + // [pasteItems[0] stringForType:NSPasteboardTypeString]); + logInfo(@"before paste"); + [pb clearContents]; + [pb writeObjects:pasteItems]; + lastTouchedCount = [pb changeCount]; + NSMutableArray *newPasteItems = copy_paste_items(pasteItems); + [pasteItems release]; + pasteItems = newPasteItems; + logInfo(@"after paste"); + } + // Mouse click to focus and position insertion cursor. - CGPoint mouseLocation = CGEventGetLocation( event ); + CGPoint mouseLocation = CGEventGetLocation(event); CGEventRef mouseClickDown = CGEventCreateMouseEvent( - NULL, kCGEventLeftMouseDown, mouseLocation, kCGMouseButtonLeft ); - CGEventRef mouseClickUp = CGEventCreateMouseEvent( - NULL, kCGEventLeftMouseUp, mouseLocation, kCGMouseButtonLeft ); - CGEventPost( tapH, mouseClickDown ); - CGEventPost( tapH, mouseClickUp ); - CFRelease( mouseClickDown ); - CFRelease( mouseClickUp ); - - // Allow click events time to position cursor before pasting. - usleep( 1000 ); - - // Paste. - CGEventSourceRef source = CGEventSourceCreate( kCGEventSourceStateCombinedSessionState ); - CGEventRef kbdEventPasteDown = CGEventCreateKeyboardEvent( source, kVK_ANSI_V, 1 ); - CGEventRef kbdEventPasteUp = CGEventCreateKeyboardEvent( source, kVK_ANSI_V, 0 ); - CGEventSetFlags( kbdEventPasteDown, kCGEventFlagMaskCommand ); - CGEventPost( tapA, kbdEventPasteDown ); - CGEventPost( tapA, kbdEventPasteUp ); - CFRelease( kbdEventPasteDown ); - CFRelease( kbdEventPasteUp ); - - CFRelease( source ); - } + NULL, kCGEventLeftMouseDown, mouseLocation, kCGMouseButtonLeft); + CGEventRef mouseClickUp = CGEventCreateMouseEvent( + NULL, kCGEventLeftMouseUp, mouseLocation, kCGMouseButtonLeft); + CGEventPost(tapH, mouseClickDown); + CGEventPost(tapH, mouseClickUp); + CFRelease(mouseClickDown); + CFRelease(mouseClickUp); - static void copy() { - CGEventSourceRef source = CGEventSourceCreate( kCGEventSourceStateCombinedSessionState ); - CGEventRef kbdEventDown = CGEventCreateKeyboardEvent( source, kVK_ANSI_C, 1 ); - CGEventRef kbdEventUp = CGEventCreateKeyboardEvent( source, kVK_ANSI_C, 0 ); - CGEventSetFlags( kbdEventDown, kCGEventFlagMaskCommand ); - CGEventPost( tapA, kbdEventDown ); - CGEventPost( tapA, kbdEventUp ); - CFRelease( kbdEventDown ); - CFRelease( kbdEventUp ); - CFRelease( source ); - } + // Allow click events time to position cursor before pasting. + usleep(1000); + + // Paste. + CGEventSourceRef source = + CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + CGEventRef kbdEventPasteDown = + CGEventCreateKeyboardEvent(source, kVK_ANSI_V, 1); + CGEventRef kbdEventPasteUp = + CGEventCreateKeyboardEvent(source, kVK_ANSI_V, 0); + CGEventSetFlags(kbdEventPasteDown, kCGEventFlagMaskCommand); + CGEventSetFlags(kbdEventPasteUp, kCGEventFlagMaskCommand); + CGEventPost(tapA, kbdEventPasteDown); + CGEventPost(tapA, kbdEventPasteUp); + CFRelease(kbdEventPasteDown); + CFRelease(kbdEventPasteUp); + + CFRelease(source); +} + +static void copy() { + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + lastClipCount = [pb changeCount]; + lastClipItems = copy_paste_items([pb pasteboardItems]); + // NSLog(@"SET LCI %@", [lastClipItems[0] + // stringForType:NSPasteboardTypeString]); + CGEventSourceRef source = + CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + CGEventRef kbdEventDown = CGEventCreateKeyboardEvent(source, kVK_ANSI_C, 1); + CGEventRef kbdEventUp = CGEventCreateKeyboardEvent(source, kVK_ANSI_C, 0); + CGEventSetFlags(kbdEventDown, kCGEventFlagMaskCommand); + CGEventPost(tapA, kbdEventDown); + CGEventPost(tapA, kbdEventUp); + CFRelease(kbdEventDown); + CFRelease(kbdEventUp); + CFRelease(source); +} + +static void recordClickTime() { + prevPrevClickTime = prevClickTime; + prevClickTime = curClickTime; + curClickTime = now(); +} + +static char isTripleClick() { + return (curClickTime - prevPrevClickTime) < TRIPLE_CLICK_MILLIS; +} + +static char isDoubleClick() { + return (curClickTime - prevClickTime) < DOUBLE_CLICK_MILLIS; +} + +static CGEventRef mouseCallback(CGEventTapProxy proxy, CGEventType type, + CGEventRef event, void *refcon) { + int *dontpaste = refcon; + int button; + bool clipFound = FALSE; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + // if we've copied, once we detect the copy happening store that selection + // in pasteItems and restore the old clipboard + if (lastClipCount != 0) { + if (lastClipCount != [pb changeCount]) { + if (pasteItems != NULL) { + [pasteItems release]; + } + pasteItems = copy_paste_items([pb pasteboardItems]); - static void recordClickTime() { - prevPrevClickTime = prevClickTime; - prevClickTime = curClickTime; - curClickTime = now(); + // NSLog(@"WRITING %@ to clip in first step, set %@ to selection", + // [lastClipItems[0] stringForType:NSPasteboardTypeString], + // [pasteItems[0] stringForType:NSPasteboardTypeString]); + logInfo(@"before restore"); + [pb clearContents]; + [pb writeObjects:lastClipItems]; + lastTouchedCount = [pb changeCount]; + NSMutableArray *newLastClipItems = copy_paste_items(lastClipItems); + [lastClipItems release]; + lastClipItems = newLastClipItems; + lastClipCount = 0; + logInfo(@"after restore"); } + } else if (lastTouchedCount != [pb changeCount]) { + // NSLog(@"@NEW clipboard?"); + lastTouchedCount = [pb changeCount]; + if (lastClipItems != NULL) { + [lastClipItems release]; + } + lastClipItems = copy_paste_items([pb pasteboardItems]); + } - static char isTripleClick() { - return ( curClickTime - prevPrevClickTime ) < TRIPLE_CLICK_MILLIS; + switch (type) { + case kCGEventOtherMouseDown: + button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); + NSCursor *cursor = [NSCursor currentSystemCursor]; + NSCursor *ibeam = [NSCursor IBeamCursor]; + if (*dontpaste == 0 && button == 2 && + NSEqualPoints([cursor hotSpot], [ibeam hotSpot])) { + // NSLog(@"paste %@", NSStringFromPoint( [NSEvent mouseLocation])); + paste(event); } + break; - static CGEventRef mouseCallback ( - CGEventTapProxy proxy, - CGEventType type, - CGEventRef event, - void * refcon - ) { - int* dontpaste = refcon; - int button; - switch ( type ) - { - case kCGEventOtherMouseDown: - button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); - NSCursor* cursor = [NSCursor currentSystemCursor]; - NSCursor* ibeam = [NSCursor IBeamCursor]; - if (*dontpaste == 0 && button == 2 && NSEqualPoints([cursor hotSpot] , [ibeam hotSpot] )) { - // NSLog(@"paste %@", NSStringFromPoint( [NSEvent mouseLocation])); - paste( event ); - } - break; - - case kCGEventLeftMouseDown: - //NSLog(@"down %@", NSStringFromPoint( [NSEvent mouseLocation])); - recordClickTime(); - break; - - case kCGEventLeftMouseUp: - //NSLog(@"up %@", NSStringFromPoint( [NSEvent mouseLocation])); - if (isTripleClick()) { - NSLog(@"copytrlc %@", NSStringFromPoint( [NSEvent mouseLocation])); - copy(); - } - if (isDragging) { - NSPoint clickLocation = [NSEvent mouseLocation]; - int xdiff = fabs(initialLocation.x-clickLocation.x); - int ydiff = fabs(initialLocation.y-clickLocation.y); - if (xdiff > 5 || ydiff > 5) { - //NSLog(@"copydrag %@ %@", NSStringFromPoint(initialLocation), NSStringFromPoint( [NSEvent mouseLocation])); - copy(); - } - } - isDragging = 0; - break; - - case kCGEventLeftMouseDragged: - if (!isDragging) - initialLocation = [NSEvent mouseLocation]; - isDragging = 1; - break; - - default: - break; + case kCGEventKeyDown: + if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == + kVK_ANSI_V && + (CGEventGetFlags(event) & kCGEventFlagMaskCommand)) { + if (lastClipItems != NULL) { + // prior to a keyboard initiated paste, restore the clipboard + // NSLog(@"WRITING %@ to clip in paste catcher", + // [lastClipItems[0] stringForType:NSPasteboardTypeString]); + logInfo(@"before pasteCatcher"); + [pb clearContents]; + [pb writeObjects:lastClipItems]; + lastTouchedCount = [pb changeCount]; + NSMutableArray *newLastClipItems = copy_paste_items(lastClipItems); + [lastClipItems release]; + lastClipItems = newLastClipItems; + logInfo(@"after pasteCatcher"); } - - // Pass on the event, we must not modify it anyway, we are a listener - return event; } + break; + + case kCGEventLeftMouseDown: + // NSLog(@"down %@", NSStringFromPoint( [NSEvent mouseLocation])); + recordClickTime(); + break; - int main ( - int argc, - char ** argv - ) { - CGEventMask emask; - CFMachPortRef myEventTap; - CFRunLoopSourceRef eventTapRLSrc; - - - // parse args for -n flag - int c; - int dontpaste = 0; - while ((c = getopt (argc, argv, "n")) != -1) - switch (c) - { - case 'n': - dontpaste = 1; - break; - default: - break; + case kCGEventLeftMouseUp: + // NSLog(@"up %@", NSStringFromPoint( [NSEvent mouseLocation])); + if (isDoubleClick()) { + // NSLog(@"copytrlc %@", NSStringFromPoint([NSEvent mouseLocation])); + copy(); + logInfo(@"copy double click"); + } else if (isDragging) { + NSPoint clickLocation = [NSEvent mouseLocation]; + int xdiff = fabs(initialLocation.x - clickLocation.x); + int ydiff = fabs(initialLocation.y - clickLocation.y); + if (xdiff > 5 || ydiff > 5) { + // NSLog(@"copydrag %@ %@", NSStringFromPoint(initialLocation), + // NSStringFromPoint([NSEvent mouseLocation])); + copy(); + logInfo(@"copy drag"); } + } + isDragging = 0; + break; + + case kCGEventLeftMouseDragged: + if (!isDragging) + initialLocation = [NSEvent mouseLocation]; + isDragging = 1; + break; - // We want "other" mouse button click-release, such as middle or exotic. - emask = CGEventMaskBit( kCGEventOtherMouseDown ) | - CGEventMaskBit( kCGEventLeftMouseDown ) | - CGEventMaskBit( kCGEventLeftMouseUp ) | - CGEventMaskBit( kCGEventLeftMouseDragged ); - NSApplicationLoad(); - - // Create the Tap - myEventTap = CGEventTapCreate ( - kCGSessionEventTap, // Catch all events for current user session - kCGTailAppendEventTap, // Append to end of EventTap list - kCGEventTapOptionListenOnly, // We only listen, we don't modify - emask, - & mouseCallback, - & dontpaste // dontpaste -> callback - ); - - // Create a RunLoop Source for it - eventTapRLSrc = CFMachPortCreateRunLoopSource( - kCFAllocatorDefault, - myEventTap, - 0 - ); - - // Add the source to the current RunLoop - CFRunLoopAddSource( - CFRunLoopGetCurrent(), - eventTapRLSrc, - kCFRunLoopDefaultMode - ); - - // Keep the RunLoop running forever - CFRunLoopRun(); - - // Not reached (RunLoop above never stops running) - return 0; + default: + break; + } + // Pass on the event, we must not modify it anyway, we are a listener + return event; +} + +int main(int argc, char **argv) { + CGEventMask emask; + CFMachPortRef myEventTap; + CFRunLoopSourceRef eventTapRLSrc; + + // parse args for -n flag + int c; + int dontpaste = 0; + while ((c = getopt(argc, argv, "n")) != -1) + switch (c) { + case 'n': + dontpaste = 1; + break; + default: + break; } + + // We want "other" mouse button click-release, such as middle or exotic. + emask = CGEventMaskBit(kCGEventOtherMouseDown) | + CGEventMaskBit(kCGEventOtherMouseUp) | + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventMouseMoved) | CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventLeftMouseDragged); + NSApplicationLoad(); + // Create the Tap + myEventTap = CGEventTapCreate( + kCGSessionEventTap, // Catch all events for current user session + kCGTailAppendEventTap, // Append to end of EventTap list + kCGEventTapOptionListenOnly, // We only listen, we don't modify + emask, &mouseCallback, + &dontpaste // dontpaste -> callback + ); + + // Create a RunLoop Source for it + eventTapRLSrc = + CFMachPortCreateRunLoopSource(kCFAllocatorDefault, myEventTap, 0); + + lastClipItems = + copy_paste_items([[NSPasteboard generalPasteboard] pasteboardItems]); + lastTouchedCount = [[NSPasteboard generalPasteboard] changeCount]; + logInfo(@"startup"); + // Add the source to the current RunLoop + CFRunLoopAddSource(CFRunLoopGetCurrent(), eventTapRLSrc, + kCFRunLoopDefaultMode); + + // Keep the RunLoop running forever + CFRunLoopRun(); + + // Not reached (RunLoop above never stops running) + return 0; +} From c1fa88dd75f3dd77ca63409bd482dae7598f8712 Mon Sep 17 00:00:00 2001 From: Joe Barnett Date: Fri, 7 Jun 2024 15:04:15 -0700 Subject: [PATCH 7/7] Tweak the state machine I think this works better? Possibly seeing some times when a double click followed by a copy doesn't fill the regular clipboard, but mostly works? Have to live with it a little longer to tell... --- macpaste.m | 60 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/macpaste.m b/macpaste.m index 276b1c8..f3aa745 100644 --- a/macpaste.m +++ b/macpaste.m @@ -22,6 +22,7 @@ #include // gettimeofday char isDragging = 0; +char debug = 0; long long prevPrevClickTime = 0; long long prevClickTime = 0; long long curClickTime = 0; @@ -61,22 +62,24 @@ long long now() { } static void logInfo(NSString *location) { - // NSString *pinfo = NULL; - // NSString *linfo = NULL; - // NSString *cinfo = NULL; - // if (pasteItems != NULL && [pasteItems count]) { - // pinfo = [pasteItems[0] stringForType:NSPasteboardTypeString]; - // } - // if (lastClipItems != NULL && [lastClipItems count]) { - // linfo = [lastClipItems[0] stringForType:NSPasteboardTypeString]; - // } - // if ([[[NSPasteboard generalPasteboard] pasteboardItems] count]) { - // cinfo = [[[NSPasteboard generalPasteboard] pasteboardItems][0] - // stringForType:NSPasteboardTypeString]; - // } - // NSLog(@"%@: %ld\npaste: %@\nlastclip %@\nactual clip: %@ (%ld)", location, - // lastTouchedCount, pinfo, linfo, cinfo, [[NSPasteboard - // generalPasteboard] changeCount]); + if (debug) { + NSString *pinfo = NULL; + NSString *linfo = NULL; + NSString *cinfo = NULL; + if (pasteItems != NULL && [pasteItems count]) { + pinfo = [pasteItems[0] stringForType:NSPasteboardTypeString]; + } + if (lastClipItems != NULL && [lastClipItems count]) { + linfo = [lastClipItems[0] stringForType:NSPasteboardTypeString]; + } + if ([[[NSPasteboard generalPasteboard] pasteboardItems] count]) { + cinfo = [[[NSPasteboard generalPasteboard] pasteboardItems][0] + stringForType:NSPasteboardTypeString]; + } + NSLog(@"%@: %ld\npaste: %@\nlastclip %@ (%ld)\nactual clip: %@ (%ld)", location, + lastTouchedCount, pinfo, linfo, lastClipCount, cinfo, [[NSPasteboard + generalPasteboard] changeCount]); + } } static void paste(CGEventRef event) { @@ -130,7 +133,7 @@ static void paste(CGEventRef event) { static void copy() { NSPasteboard *pb = [NSPasteboard generalPasteboard]; lastClipCount = [pb changeCount]; - lastClipItems = copy_paste_items([pb pasteboardItems]); + //lastClipItems = copy_paste_items([pb pasteboardItems]); // NSLog(@"SET LCI %@", [lastClipItems[0] // stringForType:NSPasteboardTypeString]); CGEventSourceRef source = @@ -138,8 +141,8 @@ static void copy() { CGEventRef kbdEventDown = CGEventCreateKeyboardEvent(source, kVK_ANSI_C, 1); CGEventRef kbdEventUp = CGEventCreateKeyboardEvent(source, kVK_ANSI_C, 0); CGEventSetFlags(kbdEventDown, kCGEventFlagMaskCommand); - CGEventPost(tapA, kbdEventDown); - CGEventPost(tapA, kbdEventUp); + CGEventPost(tapH, kbdEventDown); + CGEventPost(tapH, kbdEventUp); CFRelease(kbdEventDown); CFRelease(kbdEventUp); CFRelease(source); @@ -173,19 +176,20 @@ static CGEventRef mouseCallback(CGEventTapProxy proxy, CGEventType type, [pasteItems release]; } pasteItems = copy_paste_items([pb pasteboardItems]); + lastTouchedCount = [pb changeCount]; // NSLog(@"WRITING %@ to clip in first step, set %@ to selection", // [lastClipItems[0] stringForType:NSPasteboardTypeString], // [pasteItems[0] stringForType:NSPasteboardTypeString]); - logInfo(@"before restore"); - [pb clearContents]; - [pb writeObjects:lastClipItems]; - lastTouchedCount = [pb changeCount]; - NSMutableArray *newLastClipItems = copy_paste_items(lastClipItems); - [lastClipItems release]; - lastClipItems = newLastClipItems; + // logInfo(@"before restore"); + // [pb clearContents]; + // [pb writeObjects:lastClipItems]; + // lastTouchedCount = [pb changeCount]; + // NSMutableArray *newLastClipItems = copy_paste_items(lastClipItems); + // [lastClipItems release]; + // lastClipItems = newLastClipItems; lastClipCount = 0; - logInfo(@"after restore"); + // logInfo(@"after restore"); } } else if (lastTouchedCount != [pb changeCount]) { // NSLog(@"@NEW clipboard?"); @@ -194,6 +198,7 @@ static CGEventRef mouseCallback(CGEventTapProxy proxy, CGEventType type, [lastClipItems release]; } lastClipItems = copy_paste_items([pb pasteboardItems]); + logInfo(@"Detected new clipboard"); } switch (type) { @@ -209,6 +214,7 @@ static CGEventRef mouseCallback(CGEventTapProxy proxy, CGEventType type, break; case kCGEventKeyDown: + // NSLog(@"EVENT key: %lld (%lld)", CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode), CGEventGetFlags(event)); if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == kVK_ANSI_V && (CGEventGetFlags(event) & kCGEventFlagMaskCommand)) {