diff --git a/MTMR/AppDelegate.swift b/MTMR/AppDelegate.swift index f9c5084..7b7a5ae 100644 --- a/MTMR/AppDelegate.swift +++ b/MTMR/AppDelegate.swift @@ -25,7 +25,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary) TouchBarController.shared.setupControlStripPresence() - HapticFeedbackUpdate() if let button = statusItem.button { button.image = #imageLiteral(resourceName: "StatusImage") @@ -41,10 +40,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationWillTerminate(_: Notification) {} - func HapticFeedbackUpdate() { - HapticFeedback.shared = AppSettings.hapticFeedbackState ? HapticFeedback() : nil - } - @objc func updateIsBlockedApp() { if let frontmostAppId = TouchBarController.shared.frontmostApplicationIdentifier { isBlockedApp = AppSettings.blacklistedAppIds.firstIndex(of: frontmostAppId) != nil @@ -86,7 +81,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { @objc func toggleHapticFeedback(_ item: NSMenuItem) { item.state = item.state == .on ? .off : .on AppSettings.hapticFeedbackState = item.state == .on - HapticFeedbackUpdate() } @objc func toggleMultitouch(_ item: NSMenuItem) { diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index a10976d..ea084a8 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -240,12 +240,12 @@ final class MultiClickGestureRecognizer: NSClickGestureRecognizer { } override func touchesBegan(with event: NSEvent) { - HapticFeedback.shared?.tap(strong: 2) + HapticFeedback.instance.tap(type: .click) super.touchesBegan(with: event) } override func touchesEnded(with event: NSEvent) { - HapticFeedback.shared?.tap(strong: 1) + HapticFeedback.instance.tap(type: .back) super.touchesEnded(with: event) _clickCount += 1 @@ -322,7 +322,7 @@ class LongPressGestureRecognizer: NSPressGestureRecognizer { @objc private func onTimer() { if let target = self.target, let action = self.action { target.performSelector(onMainThread: action, with: self, waitUntilDone: false) - HapticFeedback.shared?.tap(strong: 6) + HapticFeedback.instance.tap(type: .strong) } } diff --git a/MTMR/HapticFeedback.swift b/MTMR/HapticFeedback.swift index 38a84d0..5df6c85 100644 --- a/MTMR/HapticFeedback.swift +++ b/MTMR/HapticFeedback.swift @@ -9,81 +9,89 @@ import IOKit class HapticFeedback { - static var shared: HapticFeedback? - + // Here we have list of possible IDs for Haptic Generator Device. They are not constant // To find deviceID, you will need IORegistryExplorer app from Additional Tools for Xcode dmg // which you can download from https://developer.apple.com/download/more/?=Additional%20Tools // Open IORegistryExplorer app, search for AppleMultitouchDevice and get "Multitouch ID" // There should be programmatic way to get it but I can't find, no docs for macOS :( private let possibleDeviceIDs: [UInt64] = [ - 0x200_0000_0100_0000, // MacBook Pro 2016/2017 - 0x300000080500000 // MacBook Pro 2019 (possibly 2018 as well) + 0x200_0000_0100_0000, // MacBook Pro 2016/2017 + 0x300_0000_8050_0000, // MacBook Pro 2019/2018 + 0x200_0000_0000_0024, // MacBook Pro (13-inch, M1, 2020) ] - private var correctDeviceID: UInt64? - private var actuatorRef: CFTypeRef? - - init() { - recreateDevice() + + // you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p` + enum HapticType: Int32, CaseIterable { + case back = 1 + case click = 2 + case weak = 3 + case medium = 4 + case weakMedium = 5 + case strong = 6 + case reserved1 = 15 + case reserved2 = 16 } - // Don't know how to do strong is enum one of - // 1 like back Click - // 2 like Click - // 3 week - // 4 medium - // 5 week medium - // 6 strong - // 15 nothing - // 16 nothing - // you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p` + private var actuatorRef: CFTypeRef? - func tap(strong: Int32) { - guard correctDeviceID != nil, actuatorRef != nil else { + static var instance = HapticFeedback() + + // MARK: - Init + + private init() { + self.recreateDevice() + } + + private func recreateDevice() { + if let actuatorRef = self.actuatorRef { + MTActuatorClose(actuatorRef) + self.actuatorRef = nil // just in case %) + } + + guard self.actuatorRef == nil else { + return + } + + // Let's find our Haptic device + self.possibleDeviceIDs.forEach {(deviceID) in + let actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue() + + if actuatorRef != nil { + self.actuatorRef = actuatorRef + } + } + } + + // MARK: - Tap action + + private func getActuatorIfPosible() -> CFTypeRef? { + guard AppSettings.hapticFeedbackState else { return nil } + guard let actuatorRef = self.actuatorRef else { print("guard actuatorRef == nil (no haptic device found?)") - return + return nil } - var result: IOReturn - - result = MTActuatorOpen(actuatorRef!) - guard result == kIOReturnSuccess else { + guard MTActuatorOpen(actuatorRef) == kIOReturnSuccess else { print("guard MTActuatorOpen") - recreateDevice() - return + self.recreateDevice() + return nil } - result = MTActuatorActuate(actuatorRef!, strong, 0, 0, 0) - guard result == kIOReturnSuccess else { + return actuatorRef + } + + func tap(type: HapticType) { + guard let actuator = getActuatorIfPosible() else { return } + + guard MTActuatorActuate(actuator, type.rawValue, 0, 0, 0) == kIOReturnSuccess else { print("guard MTActuatorActuate") return } - result = MTActuatorClose(actuatorRef!) - guard result == kIOReturnSuccess else { + guard MTActuatorClose(actuator) == kIOReturnSuccess else { print("guard MTActuatorClose") return } } - - private func recreateDevice() { - if let actuatorRef = actuatorRef { - MTActuatorClose(actuatorRef) - self.actuatorRef = nil // just in case %) - } - - if let correctDeviceID = correctDeviceID { - actuatorRef = MTActuatorCreateFromDeviceID(correctDeviceID).takeRetainedValue() - } else { - // Let's find our Haptic device - possibleDeviceIDs.forEach {(deviceID) in - guard correctDeviceID == nil else {return} - actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue() - - if actuatorRef != nil { - correctDeviceID = deviceID - } - } - } - } }