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..987a1ab 100644 --- a/MTMR/HapticFeedback.swift +++ b/MTMR/HapticFeedback.swift @@ -9,8 +9,7 @@ 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 @@ -18,72 +17,88 @@ class HapticFeedback { // 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) + 0x300000080500000, // MacBook Pro 2019 (possibly 2018 as well) + 0x200000000000024 // 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 correctDeviceID: UInt64? + private var actuatorRef: CFTypeRef? - func tap(strong: Int32) { - guard correctDeviceID != nil, actuatorRef != nil else { + static var instance = HapticFeedback() + + // MARK: - Init + + private init() { + self.recreateDevice() + // HapticFeedback.shared = AppSettings.hapticFeedbackState ? HapticFeedback() : nil + } + + private func recreateDevice() { + if let actuatorRef = self.actuatorRef { + MTActuatorClose(actuatorRef) + self.actuatorRef = nil // just in case %) + } + + if let correctDeviceID = self.correctDeviceID { + self.actuatorRef = MTActuatorCreateFromDeviceID(correctDeviceID).takeRetainedValue() + } else { + // Let's find our Haptic device + self.possibleDeviceIDs.forEach {(deviceID) in + guard self.correctDeviceID == nil else {return} + self.actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue() + + if self.actuatorRef != nil { + self.correctDeviceID = deviceID + } + } + } + } + + // MARK: - Tap action + + func tap(type: HapticType) { + guard AppSettings.hapticFeedbackState else { + // Haptic feedback is disabled by user + return + } + + guard self.correctDeviceID != nil, let actuatorRef = self.actuatorRef else { print("guard actuatorRef == nil (no haptic device found?)") return } var result: IOReturn - - result = MTActuatorOpen(actuatorRef!) + + result = MTActuatorOpen(actuatorRef) guard result == kIOReturnSuccess else { print("guard MTActuatorOpen") - recreateDevice() + self.recreateDevice() return } - result = MTActuatorActuate(actuatorRef!, strong, 0, 0, 0) + print("Try tap with: \(type.rawValue)") + result = MTActuatorActuate(actuatorRef, type.rawValue, 0, 0, 0) guard result == kIOReturnSuccess else { print("guard MTActuatorActuate") return } - result = MTActuatorClose(actuatorRef!) + result = MTActuatorClose(actuatorRef) guard result == 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 - } - } - } - } }