From a8bdba2ad0b757742ffb558f70b54cd5f29f19e2 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Sat, 16 Nov 2024 13:35:55 -0800 Subject: [PATCH 01/10] add ability for users to configure exception applications change main source file from C to Objective-C --- DiscreteScroll.xcodeproj/project.pbxproj | 8 ++++---- DiscreteScroll/{main.c => main.m} | 24 +++++++++++++++++++++++- README.md | 5 +++++ 3 files changed, 32 insertions(+), 5 deletions(-) rename DiscreteScroll/{main.c => main.m} (80%) diff --git a/DiscreteScroll.xcodeproj/project.pbxproj b/DiscreteScroll.xcodeproj/project.pbxproj index 70f3a6f..ac9cfa4 100644 --- a/DiscreteScroll.xcodeproj/project.pbxproj +++ b/DiscreteScroll.xcodeproj/project.pbxproj @@ -7,15 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - C3F94D482C0406330051923E /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = C3F94D472C0406330051923E /* main.c */; }; + 948CB3262CE4B90D0024F5A6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 948CB3252CE4B2410024F5A6 /* main.m */; }; C3F94D562C0408930051923E /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = C3F94D542C0408930051923E /* LICENSE */; }; C3F94D572C0408930051923E /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = C3F94D552C0408930051923E /* README.md */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 948CB3252CE4B2410024F5A6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; C3F94D3B2C0406320051923E /* DiscreteScroll.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DiscreteScroll.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3F94D462C0406330051923E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C3F94D472C0406330051923E /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; C3F94D542C0408930051923E /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; C3F94D552C0408930051923E /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ @@ -53,7 +53,7 @@ isa = PBXGroup; children = ( C3F94D462C0406330051923E /* Info.plist */, - C3F94D472C0406330051923E /* main.c */, + 948CB3252CE4B2410024F5A6 /* main.m */, ); path = DiscreteScroll; sourceTree = ""; @@ -126,7 +126,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C3F94D482C0406330051923E /* main.c in Sources */, + 948CB3262CE4B90D0024F5A6 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DiscreteScroll/main.c b/DiscreteScroll/main.m similarity index 80% rename from DiscreteScroll/main.c rename to DiscreteScroll/main.m index 52fbc36..3ba3cfa 100644 --- a/DiscreteScroll/main.c +++ b/DiscreteScroll/main.m @@ -1,3 +1,5 @@ +#import + #include #define DEFAULT_LINES 3 @@ -8,10 +10,16 @@ static bool TRUSTED; static int LINES; +static CFArrayRef EXCEPTIONS; + static CGEventRef tapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *userInfo) { - if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0) { + NSRunningApplication *focused = [[NSWorkspace sharedWorkspace] frontmostApplication]; + CFRange range = CFRangeMake(0, CFArrayGetCount(EXCEPTIONS)); + bool exceptFlag = CFArrayContainsValue(EXCEPTIONS, range, (__bridge const void *)(focused.localizedName)); + + if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0 && !exceptFlag) { int delta = (int)CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1); CGEventSetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1, SIGN(delta) * LINES); } @@ -44,6 +52,17 @@ static void notificationCallback(CFNotificationCenterRef center, void *observer, ); } +static bool getExceptionApps(CFStringRef key) +{ + EXCEPTIONS = (CFArrayRef)CFPreferencesCopyAppValue(key, kCFPreferencesCurrentApplication); + bool got = false; + if (EXCEPTIONS && CFGetTypeID(EXCEPTIONS) == CFArrayGetTypeID()) { + got = (int)CFArrayGetCount(EXCEPTIONS); + } + + return got; +} + static bool getIntPreference(CFStringRef key, int *valuePtr) { CFNumberRef number = (CFNumberRef)CFPreferencesCopyAppValue( @@ -78,6 +97,9 @@ int main(void) CFRunLoopRun(); CFNotificationCenterRemoveObserver(center, &observer, AX_NOTIFICATION, NULL); + if (!getExceptionApps(CFSTR("except"))) + EXCEPTIONS = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks); + if (!getIntPreference(CFSTR("lines"), &LINES)) LINES = DEFAULT_LINES; diff --git a/README.md b/README.md index d0125fe..9fe2f2b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,11 @@ This number may even be negative, which inverts scrolling direction. defaults write com.emreyolcu.DiscreteScroll lines -int LINES ``` +You may configure exception applications if you do not want to run DiscreteScroll on every application. Running this command repeatedly will append its arguments to the existing list of exception applications. Replace `APPLICATION` with the name of the exception application you would like to configure, e.g. `Finder`. +``` +defaults write com.emreyolcu.DiscreteScroll except -array-add "APPLICATION" "APPLICATION" +``` + > [!WARNING] > If you set `lines` to some value other than an integer, > then the default value of 3 is used as a fallback. From 1b0845e686ae9183869baa9faea5f24a115df962 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Sat, 16 Nov 2024 15:18:50 -0800 Subject: [PATCH 02/10] update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9fe2f2b..cda1d4b 100644 --- a/README.md +++ b/README.md @@ -50,16 +50,16 @@ This number may even be negative, which inverts scrolling direction. defaults write com.emreyolcu.DiscreteScroll lines -int LINES ``` -You may configure exception applications if you do not want to run DiscreteScroll on every application. Running this command repeatedly will append its arguments to the existing list of exception applications. Replace `APPLICATION` with the name of the exception application you would like to configure, e.g. `Finder`. -``` -defaults write com.emreyolcu.DiscreteScroll except -array-add "APPLICATION" "APPLICATION" -``` - > [!WARNING] > If you set `lines` to some value other than an integer, > then the default value of 3 is used as a fallback. -You should restart the application for the setting to take effect. +You may configure exception applications if you do not want DiscreteScroll to run on every application. Running this command repeatedly will append its arguments to the existing list of exception applications. Replace `APPLICATION` with the name of the exception application you would like to configure, e.g. `Finder`. +``` +defaults write com.emreyolcu.DiscreteScroll except -array-add "APPLICATION" +``` + +You should restart the application for changes in settings to take effect. ### Uninstallation From 0a59d11cfc4ab7bd897364ca2391a06063ca4820 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Wed, 27 Nov 2024 05:44:12 -0800 Subject: [PATCH 03/10] fix project warnings increase minimum macOS deployment version as suggested by Xcode fixes warning: The macOS deployment target `MACOSX_DEPLOYMENT_TARGET` is set to 10.9, but the range of supported deployment target versions is 10.13 to 15.1.99. enable dead code stripping & user script sandboxing as suggested by Xcode fixes warning: Update to recommended settings --- DiscreteScroll.xcodeproj/project.pbxproj | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/DiscreteScroll.xcodeproj/project.pbxproj b/DiscreteScroll.xcodeproj/project.pbxproj index ac9cfa4..0fa84c5 100644 --- a/DiscreteScroll.xcodeproj/project.pbxproj +++ b/DiscreteScroll.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -84,7 +84,8 @@ C3F94D332C0406320051923E /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1240; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1610; TargetAttributes = { C3F94D3A2C0406320051923E = { CreatedOnToolsVersion = 12.4; @@ -167,9 +168,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -226,9 +229,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -250,12 +255,13 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 5; + DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = DiscreteScroll/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 11.5; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -268,12 +274,13 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 5; + DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = DiscreteScroll/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 11.5; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; From ec1be2159f7d10fe82de3c833dd66092a2573454 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Wed, 27 Nov 2024 05:50:27 -0800 Subject: [PATCH 04/10] share scheme data as intended build scheme should be checked into repo as it is configured to be shared data --- .../xcschemes/DiscreteScroll.xcscheme | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme diff --git a/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme new file mode 100644 index 0000000..dcce0b7 --- /dev/null +++ b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From be609c3961525c31633be4500e6204537f3365b0 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Tue, 30 Sep 2025 21:12:41 -0700 Subject: [PATCH 05/10] rework exception app handling check for the app that is actually being scrolled rather than the active app --- DiscreteScroll/main.m | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/DiscreteScroll/main.m b/DiscreteScroll/main.m index 3ba3cfa..745f17b 100644 --- a/DiscreteScroll/main.m +++ b/DiscreteScroll/main.m @@ -1,6 +1,6 @@ #import - #include +#import #define DEFAULT_LINES 3 #define SIGN(x) (((x) > 0) - ((x) < 0)) @@ -12,12 +12,40 @@ static CFArrayRef EXCEPTIONS; +static NSString *getAppBeingScrolled (CGEventRef event) +{ + CGPoint location = CGEventGetLocation(event); + CFArrayRef windows = CGWindowListCopyWindowInfo( + kCGWindowListOptionOnScreenOnly | kCGWindowListOptionIncludingWindow, + kCGNullWindowID + ); + + NSDictionary *appInfo = nil; + for (NSDictionary *window in (__bridge NSArray *)windows) { + CGRect bounds; + CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)window[(id)kCGWindowBounds], &bounds); + + if (CGRectContainsPoint(bounds, location)) { + appInfo = window; + break; + } + } + CFRelease(windows); + + if (appInfo) { + NSString *ownerName = appInfo[(id)kCGWindowOwnerName]; + return ownerName; + } else { + return nil; + } +} + static CGEventRef tapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *userInfo) { - NSRunningApplication *focused = [[NSWorkspace sharedWorkspace] frontmostApplication]; + NSString *app = getAppBeingScrolled(event); CFRange range = CFRangeMake(0, CFArrayGetCount(EXCEPTIONS)); - bool exceptFlag = CFArrayContainsValue(EXCEPTIONS, range, (__bridge const void *)(focused.localizedName)); + bool exceptFlag = (app && CFArrayContainsValue(EXCEPTIONS, range, (__bridge CFStringRef)app)); if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0 && !exceptFlag) { int delta = (int)CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1); From e5045cbd7fbbee8e23d5a486f52468a9163b5b04 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Tue, 30 Sep 2025 21:13:04 -0700 Subject: [PATCH 06/10] update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ba77ffc..d5625dc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Emre Yolcu +Copyright (c) 2025 Emre Yolcu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 5e4ae181bfee3b6fe19a53b6e6d002301a273df7 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Tue, 30 Sep 2025 21:23:41 -0700 Subject: [PATCH 07/10] remove unnecessary imports --- DiscreteScroll/main.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/DiscreteScroll/main.m b/DiscreteScroll/main.m index 745f17b..7a4791d 100644 --- a/DiscreteScroll/main.m +++ b/DiscreteScroll/main.m @@ -1,6 +1,4 @@ -#import #include -#import #define DEFAULT_LINES 3 #define SIGN(x) (((x) > 0) - ((x) < 0)) From da56cf02784a0c0b5f1e8d21c0d89115b2012e07 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Tue, 30 Sep 2025 21:24:30 -0700 Subject: [PATCH 08/10] add .DS_Store to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c545112..a1711ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ xcuserdata/ +.DS_Store From bb71a195828d9b5f8028a913f7b0f57a44b91b02 Mon Sep 17 00:00:00 2001 From: Jacob Thompson Date: Tue, 30 Sep 2025 23:50:22 -0700 Subject: [PATCH 09/10] ignore modernization check --- DiscreteScroll.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/DiscreteScroll.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DiscreteScroll.xcodeproj/project.pbxproj b/DiscreteScroll.xcodeproj/project.pbxproj index 0fa84c5..43646e6 100644 --- a/DiscreteScroll.xcodeproj/project.pbxproj +++ b/DiscreteScroll.xcodeproj/project.pbxproj @@ -85,7 +85,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1610; + LastUpgradeCheck = 2600; TargetAttributes = { C3F94D3A2C0406320051923E = { CreatedOnToolsVersion = 12.4; diff --git a/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme index dcce0b7..7e78bec 100644 --- a/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme +++ b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme @@ -1,6 +1,6 @@ Date: Wed, 1 Oct 2025 00:01:43 -0700 Subject: [PATCH 10/10] set minimum deployment version to 10.13 --- DiscreteScroll.xcodeproj/project.pbxproj | 13 +++---------- README.md | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/DiscreteScroll.xcodeproj/project.pbxproj b/DiscreteScroll.xcodeproj/project.pbxproj index 43646e6..fe1b803 100644 --- a/DiscreteScroll.xcodeproj/project.pbxproj +++ b/DiscreteScroll.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -84,7 +84,6 @@ C3F94D332C0406320051923E /* Project object */ = { isa = PBXProject; attributes = { - BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 2600; TargetAttributes = { C3F94D3A2C0406320051923E = { @@ -168,11 +167,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -229,11 +226,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -255,13 +250,12 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 5; - DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = DiscreteScroll/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 11.5; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -274,13 +268,12 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 5; - DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = DiscreteScroll/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 11.5; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/README.md b/README.md index cda1d4b..fa20091 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ a constant number of lines with each notch of the wheel. ### Supported versions -As of November 2024, this application works on macOS versions 10.9–15.0. +As of October 2025, this application works on macOS versions `10.13`–`26.0.99`. ### Installation