From c4991a667177cc51c101d8d91267836cad86d4cd Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Tue, 31 Aug 2021 23:23:29 -0400 Subject: [PATCH] Add the app --- .gitignore | 77 ++ ScrollSwitcher.xcodeproj/project.pbxproj | 63 +- ScrollSwitcher/AppDelegate.swift | 84 +- ScrollSwitcher/Base.lproj/Main.storyboard | 717 ------------------ ScrollSwitcher/Info.plist | 8 + .../ScrollSwitcher-Bridging-Header.h | 13 + ScrollSwitcher/ViewController.swift | 26 - ScrollSwitcher/main.swift | 12 + 8 files changed, 220 insertions(+), 780 deletions(-) create mode 100644 .gitignore delete mode 100644 ScrollSwitcher/Base.lproj/Main.storyboard create mode 100644 ScrollSwitcher/Info.plist create mode 100644 ScrollSwitcher/ScrollSwitcher-Bridging-Header.h delete mode 100644 ScrollSwitcher/ViewController.swift create mode 100644 ScrollSwitcher/main.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71c8374 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +.DS_Store + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +.build/ + +# CocoaPods - Refactored to standalone file + +# Carthage - Refactored to standalone file + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +### Xcode ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated + +## Various settings + +## Other + +### Xcode Patch ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno diff --git a/ScrollSwitcher.xcodeproj/project.pbxproj b/ScrollSwitcher.xcodeproj/project.pbxproj index 992df50..625d87c 100644 --- a/ScrollSwitcher.xcodeproj/project.pbxproj +++ b/ScrollSwitcher.xcodeproj/project.pbxproj @@ -8,17 +8,19 @@ /* Begin PBXBuildFile section */ D62D7D7F26DE7A9D001DCC5F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D7D7E26DE7A9D001DCC5F /* AppDelegate.swift */; }; - D62D7D8126DE7A9D001DCC5F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D7D8026DE7A9D001DCC5F /* ViewController.swift */; }; D62D7D8326DE7A9F001DCC5F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D62D7D8226DE7A9F001DCC5F /* Assets.xcassets */; }; - D62D7D8626DE7A9F001DCC5F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D62D7D8426DE7A9F001DCC5F /* Main.storyboard */; }; + D62D7D9226DF237A001DCC5F /* PreferencePanesSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D62D7D9126DF237A001DCC5F /* PreferencePanesSupport.framework */; }; + D6955CDC26DF287800EB0723 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6955CDB26DF287800EB0723 /* main.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ D62D7D7B26DE7A9D001DCC5F /* ScrollSwitcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScrollSwitcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; D62D7D7E26DE7A9D001DCC5F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - D62D7D8026DE7A9D001DCC5F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; D62D7D8226DE7A9F001DCC5F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - D62D7D8526DE7A9F001DCC5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + D62D7D8C26DE7B36001DCC5F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + D62D7D8F26DF228D001DCC5F /* ScrollSwitcher-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ScrollSwitcher-Bridging-Header.h"; sourceTree = ""; }; + D62D7D9126DF237A001DCC5F /* PreferencePanesSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanesSupport.framework; path = ../../../../System/Library/PrivateFrameworks/PreferencePanesSupport.framework; sourceTree = ""; }; + D6955CDB26DF287800EB0723 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -26,6 +28,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D62D7D9226DF237A001DCC5F /* PreferencePanesSupport.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -37,6 +40,7 @@ children = ( D62D7D7D26DE7A9D001DCC5F /* ScrollSwitcher */, D62D7D7C26DE7A9D001DCC5F /* Products */, + D62D7D9026DF237A001DCC5F /* Frameworks */, ); sourceTree = ""; }; @@ -51,14 +55,23 @@ D62D7D7D26DE7A9D001DCC5F /* ScrollSwitcher */ = { isa = PBXGroup; children = ( + D62D7D8C26DE7B36001DCC5F /* Info.plist */, + D6955CDB26DF287800EB0723 /* main.swift */, D62D7D7E26DE7A9D001DCC5F /* AppDelegate.swift */, - D62D7D8026DE7A9D001DCC5F /* ViewController.swift */, + D62D7D8F26DF228D001DCC5F /* ScrollSwitcher-Bridging-Header.h */, D62D7D8226DE7A9F001DCC5F /* Assets.xcassets */, - D62D7D8426DE7A9F001DCC5F /* Main.storyboard */, ); path = ScrollSwitcher; sourceTree = ""; }; + D62D7D9026DF237A001DCC5F /* Frameworks */ = { + isa = PBXGroup; + children = ( + D62D7D9126DF237A001DCC5F /* PreferencePanesSupport.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -118,7 +131,6 @@ buildActionMask = 2147483647; files = ( D62D7D8326DE7A9F001DCC5F /* Assets.xcassets in Resources */, - D62D7D8626DE7A9F001DCC5F /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -129,24 +141,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D62D7D8126DE7A9D001DCC5F /* ViewController.swift in Sources */, + D6955CDC26DF287800EB0723 /* main.swift in Sources */, D62D7D7F26DE7A9D001DCC5F /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - D62D7D8426DE7A9F001DCC5F /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - D62D7D8526DE7A9F001DCC5F /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ D62D7D8726DE7A9F001DCC5F /* Debug */ = { isa = XCBuildConfiguration; @@ -272,12 +273,11 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V4WK9KR9U2; - ENABLE_APP_SANDBOX = YES; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_USER_SELECTED_FILES = readonly; + ENABLE_APP_SANDBOX = NO; + ENABLE_HARDENED_RUNTIME = NO; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScrollSwitcher/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; INFOPLIST_KEY_NSPrincipalClass = NSApplication; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -287,7 +287,12 @@ PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.ScrollSwitcher; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "ScrollSwitcher/ScrollSwitcher-Bridging-Header.h"; SWIFT_VERSION = 5.0; + SYSTEM_FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); }; name = Debug; }; @@ -300,12 +305,11 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V4WK9KR9U2; - ENABLE_APP_SANDBOX = YES; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_USER_SELECTED_FILES = readonly; + ENABLE_APP_SANDBOX = NO; + ENABLE_HARDENED_RUNTIME = NO; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScrollSwitcher/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; INFOPLIST_KEY_NSPrincipalClass = NSApplication; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -315,7 +319,12 @@ PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.ScrollSwitcher; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "ScrollSwitcher/ScrollSwitcher-Bridging-Header.h"; SWIFT_VERSION = 5.0; + SYSTEM_FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); }; name = Release; }; diff --git a/ScrollSwitcher/AppDelegate.swift b/ScrollSwitcher/AppDelegate.swift index 592986b..0cf567a 100644 --- a/ScrollSwitcher/AppDelegate.swift +++ b/ScrollSwitcher/AppDelegate.swift @@ -7,24 +7,88 @@ import Cocoa -@main +let defaultsKey = "com.apple.swipescrolldirection" + class AppDelegate: NSObject, NSApplicationDelegate { - - + // things that need to be retained to keep them from disappearing + private var prefPanesSupport: Bundle! + private var item: NSStatusItem! + private var observation: NSKeyValueObservation? func applicationDidFinishLaunching(_ aNotification: Notification) { - // Insert code here to initialize your application + + // this is where _setSwipeScrollDirection is defined + prefPanesSupport = Bundle(path: "/System/Library/PrivateFrameworks/PreferencePanesSupport.framework") + try! prefPanesSupport!.loadAndReturnError() + + item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) + item.button!.target = self + item.button!.action = #selector(toggleScrollDirection) + updateIcon() + + // update the icon when system prefs changes + DistributedNotificationCenter.default().addObserver(self, selector: #selector(updateIcon), name: .swipeScrollDirectionDidChangeNotification, object: nil) } - - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application + + @objc private func updateIcon() { + item.button!.image = Direction.current.image } - - func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true + + @objc private func toggleScrollDirection() { + setDirection(.current == Direction.natural ? .normal : .natural) + updateIcon() } + + private func setDirection(_ new: Direction) { + // can't construct a UserDefaults with NSGlobalDomain, so set it via the command line + let proc = Process() +// let out = Pipe() +// let err = Pipe() + proc.launchPath = "/usr/bin/env" + let newVal = new == .normal ? "NO" : "YES" + proc.arguments = ["defaults", "write", "-g", "com.apple.swipescrolldirection", "-bool", newVal] +// proc.standardOutput = out +// proc.standardError = err + proc.launch() + proc.waitUntilExit() +// let outData = out.fileHandleForReading.readDataToEndOfFile() +// let errData = err.fileHandleForReading.readDataToEndOfFile() + if proc.terminationStatus != 0 { + fatalError("uh oh, exit code: \(proc.terminationStatus)") + } + + // makes system preferences update + DistributedNotificationCenter.default().postNotificationName(.swipeScrollDirectionDidChangeNotification, object: nil, userInfo: nil, deliverImmediately: false) + // make the actual input behavior update + setSwipeScrollDirection(Int32(new.rawValue)) + } } +enum Direction: Int, Equatable { + case normal, natural + + static var current: Direction { + let value = UserDefaults.standard.bool(forKey: defaultsKey) + if value { + return .natural + } else { + return .normal + } + } + + var image: NSImage { + switch self { + case .normal: + return NSImage(systemSymbolName: "scroll", accessibilityDescription: nil)! + case .natural: + return NSImage(systemSymbolName: "scroll.fill", accessibilityDescription: nil)! + } + } +} + +extension Notification.Name { + static let swipeScrollDirectionDidChangeNotification = Notification.Name(rawValue: "SwipeScrollDirectionDidChangeNotification") +} diff --git a/ScrollSwitcher/Base.lproj/Main.storyboard b/ScrollSwitcher/Base.lproj/Main.storyboard deleted file mode 100644 index e127cec..0000000 --- a/ScrollSwitcher/Base.lproj/Main.storyboard +++ /dev/null @@ -1,717 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ScrollSwitcher/Info.plist b/ScrollSwitcher/Info.plist new file mode 100644 index 0000000..6f7ae58 --- /dev/null +++ b/ScrollSwitcher/Info.plist @@ -0,0 +1,8 @@ + + + + + LSUIElement + + + diff --git a/ScrollSwitcher/ScrollSwitcher-Bridging-Header.h b/ScrollSwitcher/ScrollSwitcher-Bridging-Header.h new file mode 100644 index 0000000..1dfcdaf --- /dev/null +++ b/ScrollSwitcher/ScrollSwitcher-Bridging-Header.h @@ -0,0 +1,13 @@ +// +// ScrollSwitcher-Bridging-Header.h +// ScrollSwitcher +// +// Created by Shadowfacts on 8/31/21. +// + +#ifndef ScrollSwitcher_Bridging_Header_h +#define ScrollSwitcher_Bridging_Header_h + +void setSwipeScrollDirection(int arg0); + +#endif /* ScrollSwitcher_Bridging_Header_h */ diff --git a/ScrollSwitcher/ViewController.swift b/ScrollSwitcher/ViewController.swift deleted file mode 100644 index 6acdb1f..0000000 --- a/ScrollSwitcher/ViewController.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// ViewController.swift -// ScrollSwitcher -// -// Created by Shadowfacts on 8/31/21. -// - -import Cocoa - -class ViewController: NSViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - // Do any additional setup after loading the view. - } - - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } - } - - -} - diff --git a/ScrollSwitcher/main.swift b/ScrollSwitcher/main.swift new file mode 100644 index 0000000..3ece8c1 --- /dev/null +++ b/ScrollSwitcher/main.swift @@ -0,0 +1,12 @@ +// +// main.swift +// ScrollSwitcher +// +// Created by Shadowfacts on 8/31/21. +// + +import Cocoa + +let delegate = AppDelegate() +NSApplication.shared.delegate = delegate +_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)