From ec70590c9379fd917591d10459804f06d406334f Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Sun, 26 Mar 2023 19:32:04 +0200 Subject: [PATCH 1/3] Limit how long fingers can rest on the trackpad The current limit only applies to the calculation of the distance. This means that the fingers can rest on the trackpad for an arbitrary amount and still trigger a middle click. --- MiddleClick/Controller.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MiddleClick/Controller.m b/MiddleClick/Controller.m index 2311761..0c2689b 100644 --- a/MiddleClick/Controller.m +++ b/MiddleClick/Controller.m @@ -286,8 +286,9 @@ int touchCallback(int device, Finger* data, int nFingers, double timestamp, } } else { if (nFingers == 0) { + NSTimeInterval elapsedTime = touchStartTime ? -[touchStartTime timeIntervalSinceNow] : 0; touchStartTime = NULL; - if (middleclickX + middleclickY) { + if (middleclickX + middleclickY && elapsedTime <= 0.5f) { float delta = ABS(middleclickX - middleclickX2) + ABS(middleclickY - middleclickY2); if (delta < 0.4f) { // Emulate a middle click From 2d4cc542b354e2ebb75c94a94bcf6e70b3773bca Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Sun, 26 Mar 2023 19:32:45 +0200 Subject: [PATCH 2/3] Make the thresholds for the tap detection configurable The current thresholds can easily cause conflicts with other gestures. A maximum distance of 0.4f means you can swipe on the trackpad and still trigger a middle click (the position is normalized and it is always in the range [0..1]). Lower values, such as 0.05f, work better for detecting just taps. A maximum duration of 400ms is also quite a lot for a tap. Something between 150ms/200ms works better for detecting taps. Instead of changing the default values, which maybe work for some, this adds two new preferences that allow to change those thresholds. For example, to set the maximum distance to 0.05f and the maximum duration to 200, run: defaults write com.rouge41.middleClick maxDistanceDelta 0.05 defaults write com.rouge41.middleClick maxTimeDelta 200 --- MiddleClick/Controller.m | 13 ++++++++----- MiddleClick/PreferenceKeys.h | 12 ++++++++++++ MiddleClick/TrayMenu.m | 3 ++- MiddleClick/main.m | 16 +++++++++++++++- 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 MiddleClick/PreferenceKeys.h diff --git a/MiddleClick/Controller.m b/MiddleClick/Controller.m index 0c2689b..c892c7d 100644 --- a/MiddleClick/Controller.m +++ b/MiddleClick/Controller.m @@ -1,4 +1,5 @@ #import "Controller.h" +#import "PreferenceKeys.h" #include "TrayMenu.h" #import #include @@ -66,7 +67,7 @@ - (void)start threeDown = NO; wasThreeDown = NO; - fingersQua = [[NSUserDefaults standardUserDefaults] integerForKey:@"fingers"]; + fingersQua = [[NSUserDefaults standardUserDefaults] integerForKey:kFingersNum]; NSString* needToClickNullable = [[NSUserDefaults standardUserDefaults] valueForKey:@"needClick"]; needToClick = needToClickNullable ? [[NSUserDefaults standardUserDefaults] boolForKey:@"needClick"] : [self getIsSystemTapToClickDisabled]; @@ -270,7 +271,9 @@ int touchCallback(int device, Finger* data, int nFingers, double timestamp, int frame) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - fingersQua = [[NSUserDefaults standardUserDefaults] integerForKey:@"fingers"]; + fingersQua = [[NSUserDefaults standardUserDefaults] integerForKey:kFingersNum]; + float maxDistanceDelta = [[NSUserDefaults standardUserDefaults] floatForKey:kMaxDistanceDelta]; + float maxTimeDelta = [[NSUserDefaults standardUserDefaults] integerForKey:kMaxTimeDeltaMs] / 1000.f; if (needToClick) { if (nFingers == fingersQua) { @@ -288,9 +291,9 @@ int touchCallback(int device, Finger* data, int nFingers, double timestamp, if (nFingers == 0) { NSTimeInterval elapsedTime = touchStartTime ? -[touchStartTime timeIntervalSinceNow] : 0; touchStartTime = NULL; - if (middleclickX + middleclickY && elapsedTime <= 0.5f) { + if (middleclickX + middleclickY && elapsedTime <= maxTimeDelta) { float delta = ABS(middleclickX - middleclickX2) + ABS(middleclickY - middleclickY2); - if (delta < 0.4f) { + if (delta < maxDistanceDelta) { // Emulate a middle click // get the current pointer location @@ -316,7 +319,7 @@ int touchCallback(int device, Finger* data, int nFingers, double timestamp, } else { if (maybeMiddleClick == YES) { NSTimeInterval elapsedTime = -[touchStartTime timeIntervalSinceNow]; - if (elapsedTime > 0.5f) + if (elapsedTime > maxTimeDelta) maybeMiddleClick = NO; } } diff --git a/MiddleClick/PreferenceKeys.h b/MiddleClick/PreferenceKeys.h new file mode 100644 index 0000000..b5cc81d --- /dev/null +++ b/MiddleClick/PreferenceKeys.h @@ -0,0 +1,12 @@ +// The number of fingers needed to simulate a middle click. +#define kFingersNum @"fingers" +#define kFingersNumDefault 3 + +// The maximum distance the cursor can travel between touch and release for a tap to be considered valid. +// The position is normalized and values go from 0 to 1. +#define kMaxDistanceDelta @"maxDistanceDelta" +#define kMaxDistanceDeltaDefault 0.4f + +// The maximum interval in milliseconds between touch and release for a tap to be considered valid. +#define kMaxTimeDeltaMs @"maxTimeDelta" +#define kMaxTimeDeltaMsDefault 500 diff --git a/MiddleClick/TrayMenu.m b/MiddleClick/TrayMenu.m index 301e6ca..4f56f14 100644 --- a/MiddleClick/TrayMenu.m +++ b/MiddleClick/TrayMenu.m @@ -1,4 +1,5 @@ #import "TrayMenu.h" +#import "PreferenceKeys.h" #import "Controller.h" #import @@ -74,7 +75,7 @@ - (void)setChecks bool clickMode = [myController getClickMode]; NSString* clickModeInfo = clickMode ? @"Click" : @"Click or Tap"; - int fingersQua = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"fingers"]; + int fingersQua = (int)[[NSUserDefaults standardUserDefaults] integerForKey:kFingersNum]; [infoItem setTitle:[clickModeInfo stringByAppendingFormat: @" with %d Fingers", fingersQua]]; [tapToClickItem setState:clickMode ? NSControlStateValueOff : NSControlStateValueOn]; diff --git a/MiddleClick/main.m b/MiddleClick/main.m index 9de1590..83b895e 100644 --- a/MiddleClick/main.m +++ b/MiddleClick/main.m @@ -1,10 +1,24 @@ #import "Controller.h" +#import "PreferenceKeys.h" #import "TrayMenu.h" int main(int argc, char* argv[]) { + id keys[] = { + kFingersNum, + kMaxDistanceDelta, + kMaxTimeDeltaMs, + }; + id objects[] = { + [NSNumber numberWithInt:kFingersNumDefault], + [NSNumber numberWithFloat:kMaxDistanceDeltaDefault], + [NSNumber numberWithInt:kMaxTimeDeltaMsDefault], + }; + NSUInteger count = sizeof(objects) / sizeof(id); NSDictionary *appDefaults = [NSDictionary - dictionaryWithObject:[NSNumber numberWithInt:3] forKey:@"fingers"]; + dictionaryWithObjects:objects + forKeys:keys + count:count]; [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; From d53bb3bb6b7c78e1eacd7dc2386a9bc877c5bafc Mon Sep 17 00:00:00 2001 From: Art Ginzburg Date: Sat, 1 Apr 2023 20:14:18 +0400 Subject: [PATCH 3/3] add `PreferenceKeys.h` to `project.pbxproj` --- MiddleClick.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MiddleClick.xcodeproj/project.pbxproj b/MiddleClick.xcodeproj/project.pbxproj index f21b797..e56e08f 100644 --- a/MiddleClick.xcodeproj/project.pbxproj +++ b/MiddleClick.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 75A6B69C0FEE90530071FAC0 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = MiddleClick/Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* MiddleClick.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MiddleClick.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9753175829D5983E0065CAA7 /* PreferenceKeys.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PreferenceKeys.h; path = MiddleClick/PreferenceKeys.h; sourceTree = ""; }; FAD94F9210A88A7800A520EC /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = MiddleClick/Controller.h; sourceTree = ""; }; FAD94F9310A88A7800A520EC /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = MiddleClick/Controller.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -105,6 +106,7 @@ FAD94F9210A88A7800A520EC /* Controller.h */, FAD94F9310A88A7800A520EC /* Controller.m */, 29B97316FDCFA39411CA2CEA /* main.m */, + 9753175829D5983E0065CAA7 /* PreferenceKeys.h */, ); name = "Other Sources"; sourceTree = "";