// // AppDelegate.swift // ScrollSwitcher // // Created by Shadowfacts on 8/31/21. // import Cocoa 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) { // 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) } @objc private func updateIcon() { item.button!.image = Direction.current.image } @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") }