diff --git a/.gitignore b/.gitignore index c545112..a1711ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ xcuserdata/ +.DS_Store diff --git a/DiscreteScroll.xcodeproj/project.pbxproj b/DiscreteScroll.xcodeproj/project.pbxproj index 70f3a6f..fe1b803 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 = ""; @@ -84,7 +84,7 @@ C3F94D332C0406320051923E /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1240; + LastUpgradeCheck = 2600; TargetAttributes = { C3F94D3A2C0406320051923E = { CreatedOnToolsVersion = 12.4; @@ -126,7 +126,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C3F94D482C0406330051923E /* main.c in Sources */, + 948CB3262CE4B90D0024F5A6 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -255,7 +255,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -273,7 +273,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.emreyolcu.DiscreteScroll; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme new file mode 100644 index 0000000..7e78bec --- /dev/null +++ b/DiscreteScroll.xcodeproj/xcshareddata/xcschemes/DiscreteScroll.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DiscreteScroll/main.c b/DiscreteScroll/main.m similarity index 68% rename from DiscreteScroll/main.c rename to DiscreteScroll/main.m index 52fbc36..7a4791d 100644 --- a/DiscreteScroll/main.c +++ b/DiscreteScroll/main.m @@ -8,10 +8,44 @@ static bool TRUSTED; static int LINES; +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) { - if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0) { + NSString *app = getAppBeingScrolled(event); + CFRange range = CFRangeMake(0, CFArrayGetCount(EXCEPTIONS)); + bool exceptFlag = (app && CFArrayContainsValue(EXCEPTIONS, range, (__bridge CFStringRef)app)); + + if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0 && !exceptFlag) { int delta = (int)CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1); CGEventSetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1, SIGN(delta) * LINES); } @@ -44,6 +78,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 +123,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/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 diff --git a/README.md b/README.md index d0125fe..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 @@ -54,7 +54,12 @@ defaults write com.emreyolcu.DiscreteScroll lines -int LINES > 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