diff --git a/Podfile b/Podfile index 066584b..563c27f 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,5 @@ # Uncomment the next line to define a global platform for your project -platform :osx, '10.12' +platform :osx, '10.13' target 'Unshaky' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks diff --git a/Unshaky.xcodeproj/project.pbxproj b/Unshaky.xcodeproj/project.pbxproj index e6b6f4f..8aa32fa 100644 --- a/Unshaky.xcodeproj/project.pbxproj +++ b/Unshaky.xcodeproj/project.pbxproj @@ -427,7 +427,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "LOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${CODE_SIGN_IDENTITY}\"\nIDENTITY=\"Apple Development: Xinhong Liu (32BU6UVQ76)\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n# If a code signing identity is not specified, use ad hoc signing\nIDENTITY=\"-\"\nfi\nif [ \"$DONT_CODE_SIGN\" == \"YES\" ]\nthen\n# If a code signing identity is not specified, use ad hoc signing\nIDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; + shellScript = "LOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\n\n# By default, use the configured code signing identity for the project/target\nIDENTITY=\"${CODE_SIGN_IDENTITY}\"\nif [ \"$IDENTITY\" == \"\" ]\nthen\n# If a code signing identity is not specified, use ad hoc signing\nIDENTITY=\"-\"\nfi\nif [ \"$DONT_CODE_SIGN\" == \"YES\" ]\nthen\n# If a code signing identity is not specified, use ad hoc signing\nIDENTITY=\"-\"\nfi\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; }; 63A7B0E478BADB58754CFBEC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -792,7 +792,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 0.7.0; PRODUCT_BUNDLE_IDENTIFIER = com.nestederror.Unshaky; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -821,7 +821,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 0.7.0; PRODUCT_BUNDLE_IDENTIFIER = com.nestederror.Unshaky; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Unshaky/AppDelegate.swift b/Unshaky/AppDelegate.swift index ede9dc0..12094b0 100644 --- a/Unshaky/AppDelegate.swift +++ b/Unshaky/AppDelegate.swift @@ -8,6 +8,7 @@ import Cocoa import ServiceManagement +import IOKit.hid @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @@ -88,17 +89,48 @@ class AppDelegate: NSObject, NSApplicationDelegate { // Basic // func setup() { + // Since macOS 10.15 (and strictly enforced in 10.15.6+), CGEventTap requires + // BOTH Accessibility AND Input Monitoring permission. Trigger the Input + // Monitoring prompt up-front so the user lands in the right pane. + let inputMonitoringGranted = requestInputMonitoringIfNeeded() + // this following lines will add Unshaky.app to privacy->accessibility panel, unchecked let checkOptPrompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString let accessEnabled = AXIsProcessTrustedWithOptions([checkOptPrompt: false] as CFDictionary?) - if (!shakyPressPreventer.setupEventTap() || !accessEnabled) { + let eventTapOK = shakyPressPreventer.setupEventTap() + + if !accessEnabled || !eventTapOK { let alert = NSAlert() alert.messageText = NSLocalizedString("Accessibility Help", comment: "") alert.runModal() NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!) NSApplication.shared.terminate(self) + return + } + + if !inputMonitoringGranted { + let alert = NSAlert() + alert.messageText = NSLocalizedString("Input Monitoring Help", comment: "") + alert.runModal() + NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent")!) + NSApplication.shared.terminate(self) + } + } + + /// Returns true if Input Monitoring is granted (or can't be queried on this OS). + /// On macOS 10.15+, also triggers the system prompt the first time it's needed. + private func requestInputMonitoringIfNeeded() -> Bool { + if #available(macOS 10.15, *) { + let access = IOHIDCheckAccess(kIOHIDRequestTypeListenEvent) + if access == kIOHIDAccessTypeGranted { + return true + } + // Fires the TCC prompt on first call; subsequent calls just return the + // current state without re-prompting. + return IOHIDRequestAccess(kIOHIDRequestTypeListenEvent) } + return true } func recover() { diff --git a/Unshaky/Base.lproj/Localizable.strings b/Unshaky/Base.lproj/Localizable.strings index c3ec6ed..d8500ec 100644 --- a/Unshaky/Base.lproj/Localizable.strings +++ b/Unshaky/Base.lproj/Localizable.strings @@ -14,6 +14,8 @@ "Accessibility Help" = "Unshaky needs accessibility permission to work. Right now, Unshaky will attempt to open this panel for you. If it does not open automatically, you have to go to: System Preferences -> Security & Privacy -> Privacy (Tab) -> Accessibility (Left panel). \n\nWhen you have the panel open, add and check the Unshaky.app. \n\nOn Big Sur, you will need to do the same for Privacy (Tab) -> Input Monitoring (Left panel)."; +"Input Monitoring Help" = "Unshaky also needs Input Monitoring permission on macOS 10.15.6 and later. Unshaky will attempt to open this panel for you. If it does not open automatically, go to: System Settings -> Privacy & Security -> Input Monitoring, then add and enable Unshaky.app. \n\nAfter enabling it, relaunch Unshaky."; + "Invalid Delay" = "\"%@\" is not a valid delay. Please input an integer value."; "Paste the configuration here" = "Paste the configuration here";