diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17c2b3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore +## User settings +xcuserdata/ +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +## Gcc Patch +/*.gcno + +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index e492275..a846fc8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The issue is described here: https://apple.stackexchange.com/questions/116617/how-to-separate-mouse-and-trackpad-settings -Unfortunately most/all solutions no longer work reliably if at all in Catalina, or offer much more functionality/bloat than you may want. This application solves the issue and is only mere kilobytes in size. +Unfortunately most/all solutions no longer work reliably if at all in Catalina. ## Installation @@ -33,7 +33,7 @@ brew install --cask unnaturalscrollwheels 1. Download the latest `.dmg` from the [releases page](/../../releases), mount it, and copy the `.app` to your applications folder like any other application. -2. Since the application is not notarized as I didn't want to contribute to Apple's addiction to greed, you will need to right click on the `.app` and choose Open. +2. The application is not notarized since I haven't paid the yearly $100 fee to join the Apple Developer Program, you will need to right click on the `.app` and choose Open. ![Open Application](/../master/Screenshots/OpenApplication.png?raw=true "Open Application") diff --git a/Screenshots/Screenshot.png b/Screenshots/Screenshot.png index 0b55058..17e5b96 100644 Binary files a/Screenshots/Screenshot.png and b/Screenshots/Screenshot.png differ diff --git a/UnnaturalScrollWheels.xcodeproj/project.pbxproj b/UnnaturalScrollWheels.xcodeproj/project.pbxproj index 6a27532..f8cf8bf 100644 --- a/UnnaturalScrollWheels.xcodeproj/project.pbxproj +++ b/UnnaturalScrollWheels.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 91727DB224CCBDDA00432163 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91727DB124CCBDDA00432163 /* PreferencesViewController.swift */; }; + 918546AB25C0758700B2AD21 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918546AA25C0758700B2AD21 /* MenuBarItem.swift */; }; 91C7ECE424CC86B6007E2D4C /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91C7ECE324CC86B6007E2D4C /* Options.swift */; }; 91C7ECE624CC8789007E2D4C /* ScrollInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91C7ECE524CC8789007E2D4C /* ScrollInterceptor.swift */; }; 91C8ABFB24CCA471007E85A9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 91C8ABFA24CCA471007E85A9 /* Main.storyboard */; }; @@ -16,7 +17,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 9138880425B0CB2700A3A633 /* UnnaturalScrollWheels-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnnaturalScrollWheels-Bridging-Header.h"; sourceTree = ""; }; 91727DB124CCBDDA00432163 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; + 918546AA25C0758700B2AD21 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = ""; }; 91C7ECE324CC86B6007E2D4C /* Options.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = ""; }; 91C7ECE524CC8789007E2D4C /* ScrollInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollInterceptor.swift; sourceTree = ""; }; 91C8ABFA24CCA471007E85A9 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; @@ -59,12 +62,14 @@ children = ( 91D8069D24CB37B80092C286 /* AppDelegate.swift */, 91C7ECE324CC86B6007E2D4C /* Options.swift */, - 91C8ABFA24CCA471007E85A9 /* Main.storyboard */, 91727DB124CCBDDA00432163 /* PreferencesViewController.swift */, 91C7ECE524CC8789007E2D4C /* ScrollInterceptor.swift */, + 9138880425B0CB2700A3A633 /* UnnaturalScrollWheels-Bridging-Header.h */, + 91C8ABFA24CCA471007E85A9 /* Main.storyboard */, 91D8069F24CB37BD0092C286 /* Assets.xcassets */, 91D806A424CB37BD0092C286 /* Info.plist */, 91D806A524CB37BD0092C286 /* UnnaturalScrollWheels.entitlements */, + 918546AA25C0758700B2AD21 /* MenuBarItem.swift */, ); path = UnnaturalScrollWheels; sourceTree = ""; @@ -101,6 +106,7 @@ TargetAttributes = { 91D8069924CB37B80092C286 = { CreatedOnToolsVersion = 11.6; + LastSwiftMigration = 1230; }; }; }; @@ -140,6 +146,7 @@ buildActionMask = 2147483647; files = ( 91C7ECE624CC8789007E2D4C /* ScrollInterceptor.swift in Sources */, + 918546AB25C0758700B2AD21 /* MenuBarItem.swift in Sources */, 91D8069E24CB37B80092C286 /* AppDelegate.swift in Sources */, 91C7ECE424CC86B6007E2D4C /* Options.swift in Sources */, 91727DB224CCBDDA00432163 /* PreferencesViewController.swift in Sources */, @@ -268,6 +275,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = UnnaturalScrollWheels/UnnaturalScrollWheels.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; @@ -280,9 +288,11 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.2.0; PRODUCT_BUNDLE_IDENTIFIER = com.theron.UnnaturalScrollWheels; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "UnnaturalScrollWheels/UnnaturalScrollWheels-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -291,6 +301,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = UnnaturalScrollWheels/UnnaturalScrollWheels.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; @@ -303,9 +314,10 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.2.0; PRODUCT_BUNDLE_IDENTIFIER = com.theron.UnnaturalScrollWheels; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "UnnaturalScrollWheels/UnnaturalScrollWheels-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Release; diff --git a/UnnaturalScrollWheels.xcodeproj/xcshareddata/xcschemes/UnnaturalScrollWheels.xcscheme b/UnnaturalScrollWheels.xcodeproj/xcshareddata/xcschemes/UnnaturalScrollWheels.xcscheme new file mode 100644 index 0000000..3833ea4 --- /dev/null +++ b/UnnaturalScrollWheels.xcodeproj/xcshareddata/xcschemes/UnnaturalScrollWheels.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 3220f27..88e4440 100644 --- a/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,114 @@ uuid = "6740C2C9-4E35-4EFE-A7B3-FE4CE78B91D3" type = "1" version = "2.0"> + + + + + + + + + + + + + + + + + + + + diff --git a/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcschemes/xcschememanagement.plist b/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcschemes/xcschememanagement.plist index b73a25b..37cec57 100644 --- a/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/UnnaturalScrollWheels.xcodeproj/xcuserdata/theron.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,13 @@ 0 + SuppressBuildableAutocreation + + 91D8069924CB37B80092C286 + + primary + + + diff --git a/UnnaturalScrollWheels/AppDelegate.swift b/UnnaturalScrollWheels/AppDelegate.swift index 41663ed..08ea52b 100644 --- a/UnnaturalScrollWheels/AppDelegate.swift +++ b/UnnaturalScrollWheels/AppDelegate.swift @@ -8,29 +8,25 @@ import Cocoa import Foundation +import IOKit @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { - var statusItem: NSStatusItem? + private let statusItem = MenuBarItem.shared var prefsWindow: NSWindow? @IBOutlet weak var menu: NSMenu? - @IBOutlet weak var showMenuBarItem: NSMenuItem? + //@IBOutlet weak var showMenuBarItem: NSMenuItem? @IBOutlet weak var openAtLoginItem: NSMenuItem? @IBOutlet weak var preferencesMenuItem: NSMenuItem? - @IBOutlet weak var aboutMenuItem: NSMenuItem? @IBOutlet weak var quitMenuItem: NSMenuItem? func applicationDidFinishLaunching(_ aNotification: Notification) { Options.shared.loadOptions() - statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - statusItem?.button?.title = "⭥" - showMenuBarItem?.state = Options.shared.showMenuBarIcon ? NSControl.StateValue.on : NSControl.StateValue.off - if let menu = menu { - statusItem?.menu = menu - } - showMenuBarIcon() + self.statusItem.menu = self.menu + statusItem.refreshVisibility() ScrollInterceptor.shared.interceptScroll() + disableMouseAccel() } @IBAction func openAtLoginClicked(_ sender: Any) { @@ -38,17 +34,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { NSWorkspace.shared.open(url) } - @IBAction func showMenuBarIconClicked(_ sender: Any) { - Options.shared.showMenuBarIcon = !Options.shared.showMenuBarIcon - UserDefaults.standard.set(Options.shared.showMenuBarIcon, forKey: "ShowMenuBarIcon") - showMenuBarItem?.state = Options.shared.showMenuBarIcon ? NSControl.StateValue.on : NSControl.StateValue.off - showMenuBarIcon() - } - @IBAction func preferencesClicked(_ sender: Any) { showPreferences() } + @IBAction func showAbout(_ sender: Any) { + NSApp.activate(ignoringOtherApps: true) + NSApp.orderFrontStandardAboutPanel(sender) + } + func showPreferences() // called from menu item { if prefsWindow == nil @@ -62,24 +56,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { window.center() prefsWindow = window } - statusItem?.length = NSStatusItem.variableLength prefsWindow?.makeKeyAndOrderFront(self) NSApp.activate(ignoringOtherApps: true) } - + func windowWillClose(_ notification: Notification) { prefsWindow = nil - showMenuBarIcon() } - func showMenuBarIcon() { - if Options.shared.showMenuBarIcon { - statusItem?.length = NSStatusItem.variableLength - } else { - statusItem?.length = 0.0 - } - } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { if !flag { @@ -87,4 +72,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { } return true } + + func disableMouseAccel() { + if Options.shared.disableMouseAccel { + // Based on https://github.com/apsun/NoMouseAccel + let accelNum: CFNumber = CFNumberCreate(kCFAllocatorDefault, CFNumberType.sInt32Type, &Options.shared.accel) + let client: IOHIDEventSystemClient = IOHIDEventSystemClientCreateSimpleClient(kCFAllocatorDefault) + let mouseAccelerationType: CFString = kIOHIDMouseAccelerationType as NSString + IOHIDEventSystemClientSetProperty(client, mouseAccelerationType, accelNum) + } + } } diff --git a/UnnaturalScrollWheels/DisableMouseAccel.swift b/UnnaturalScrollWheels/DisableMouseAccel.swift new file mode 100644 index 0000000..36d1804 --- /dev/null +++ b/UnnaturalScrollWheels/DisableMouseAccel.swift @@ -0,0 +1,9 @@ +// +// DisableMouseAccel.swift +// UnnaturalScrollWheels +// +// Created by Theron Tjapkes on 1/12/21. +// Copyright © 2021 Theron Tjapkes. All rights reserved. +// + +import Foundation diff --git a/UnnaturalScrollWheels/Info.plist b/UnnaturalScrollWheels/Info.plist index 5be8b44..7cecb68 100644 --- a/UnnaturalScrollWheels/Info.plist +++ b/UnnaturalScrollWheels/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1 + 30 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/UnnaturalScrollWheels/Main.storyboard b/UnnaturalScrollWheels/Main.storyboard index 7e9182b..efad0f0 100644 --- a/UnnaturalScrollWheels/Main.storyboard +++ b/UnnaturalScrollWheels/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -22,12 +22,6 @@ - - - - - - @@ -38,7 +32,7 @@ - + @@ -59,12 +53,10 @@ - - @@ -97,166 +89,296 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + diff --git a/UnnaturalScrollWheels/MenuBarItem.swift b/UnnaturalScrollWheels/MenuBarItem.swift new file mode 100644 index 0000000..b7638ff --- /dev/null +++ b/UnnaturalScrollWheels/MenuBarItem.swift @@ -0,0 +1,41 @@ +// +// MenuBarItem.swift +// UnnaturalScrollWheels +// +// Created by Theron Tjapkes on 1/26/21. +// Copyright © 2021 Theron Tjapkes. All rights reserved. +// + +import Foundation +import Cocoa + +class MenuBarItem { + + static let shared = MenuBarItem() + var statusItem: NSStatusItem? + public var menu: NSMenu? { + didSet { + statusItem?.menu = menu + } + } + + public func refreshVisibility(){ + if Options.shared.showMenuBarIcon { + add() + } else { + remove() + } + } + + private func add() { + statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) + statusItem?.button?.title = "⭥" + statusItem?.menu = self.menu + } + + private func remove() { + guard let statusItem = statusItem else { return } + NSStatusBar.system.removeStatusItem(statusItem) + } +} + diff --git a/UnnaturalScrollWheels/Options.swift b/UnnaturalScrollWheels/Options.swift index 175898e..0d7f6b4 100644 --- a/UnnaturalScrollWheels/Options.swift +++ b/UnnaturalScrollWheels/Options.swift @@ -8,12 +8,14 @@ import Foundation class Options { static let shared = Options() + var accel: Int32 = -1 var showMenuBarIcon: Bool = true var invertVerticalScroll: Bool = true var invertHorizontalScroll: Bool = false var disableScrollAccel: Bool = true var scrollLines: Int64 = 3 var alternateDetectionMethod: Bool = false + var disableMouseAccel: Bool = false init() { if UserDefaults.standard.object(forKey: "ShowMenuBarIcon") == nil { @@ -34,6 +36,9 @@ class Options { if UserDefaults.standard.object(forKey: "AlternateDetectionMethod") == nil { UserDefaults.standard.set(alternateDetectionMethod, forKey: "AlternateDetectionMethod") } + if UserDefaults.standard.object(forKey: "DisableMouseAccel") == nil { + UserDefaults.standard.set(disableMouseAccel, forKey: "DisableMouseAccel") + } loadOptions() } @@ -44,6 +49,6 @@ class Options { disableScrollAccel = UserDefaults.standard.bool(forKey: "DisableScrollAccel") scrollLines = Int64(UserDefaults.standard.integer(forKey: "ScrollLines")) alternateDetectionMethod = UserDefaults.standard.bool(forKey: "AlternateDetectionMethod") + disableMouseAccel = UserDefaults.standard.bool(forKey: "DisableMouseAccel") } - } diff --git a/UnnaturalScrollWheels/PreferencesViewController.swift b/UnnaturalScrollWheels/PreferencesViewController.swift index c7defe8..415a68e 100644 --- a/UnnaturalScrollWheels/PreferencesViewController.swift +++ b/UnnaturalScrollWheels/PreferencesViewController.swift @@ -15,6 +15,8 @@ class PreferencesViewController: NSViewController { @IBOutlet weak var scrollLinesText: NSTextField? @IBOutlet weak var scrollLines: NSStepper? @IBOutlet weak var alternateDetectionMethod: NSButton? + @IBOutlet weak var disableMouseAccel: NSButton? + @IBOutlet weak var showMenuBarItem: NSButton? override func viewDidLoad() { super.viewDidLoad() @@ -24,6 +26,8 @@ class PreferencesViewController: NSViewController { scrollLines?.takeIntValueFrom(Options.shared.scrollLines) scrollLinesText?.takeStringValueFrom(scrollLines?.integerValue) alternateDetectionMethod?.takeIntValueFrom(Options.shared.alternateDetectionMethod) + disableMouseAccel?.takeIntValueFrom(Options.shared.disableMouseAccel) + showMenuBarItem?.takeIntValueFrom(Options.shared.showMenuBarIcon) } func activate(){ @@ -40,15 +44,23 @@ class PreferencesViewController: NSViewController { } @IBAction func applyPreferences(_ sender: Any) { - UserDefaults.standard.set(invertVerticalScroll!.state == NSControl.StateValue.on, forKey: "InvertVerticalScroll") + UserDefaults.standard.set(invertVerticalScroll?.state == NSControl.StateValue.on, forKey: "InvertVerticalScroll") UserDefaults.standard.set(invertHorizontalScroll?.state == NSControl.StateValue.on, forKey: "InvertHorizontalScroll") UserDefaults.standard.set(disableScrollAccel?.state == NSControl.StateValue.on, forKey: "DisableScrollAccel") UserDefaults.standard.set(scrollLines?.integerValue, forKey: "ScrollLines") UserDefaults.standard.set(alternateDetectionMethod?.state == NSControl.StateValue.on, forKey: "AlternateDetectionMethod") + UserDefaults.standard.set(disableMouseAccel?.state == NSControl.StateValue.on, forKey: "DisableMouseAccel") + UserDefaults.standard.set(showMenuBarItem?.state == NSControl.StateValue.on, forKey: "ShowMenuBarIcon") Options.shared.loadOptions() dismissPreferences(self) } + @IBAction func showMenuBarIconClicked(_ sender: Any) { + Options.shared.showMenuBarIcon = !Options.shared.showMenuBarIcon + UserDefaults.standard.set(Options.shared.showMenuBarIcon, forKey: "ShowMenuBarIcon") + showMenuBarItem?.state = Options.shared.showMenuBarIcon ? NSControl.StateValue.on : NSControl.StateValue.off + MenuBarItem.shared.refreshVisibility() + } @IBAction func dismissPreferences(_ sender: Any) { self.view.window?.performClose(self) diff --git a/UnnaturalScrollWheels/UnnaturalScrollWheels-Bridging-Header.h b/UnnaturalScrollWheels/UnnaturalScrollWheels-Bridging-Header.h new file mode 100644 index 0000000..99e3561 --- /dev/null +++ b/UnnaturalScrollWheels/UnnaturalScrollWheels-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// +#import +#import