import Carbon import Foundation /// 管理输入法切换,用于在粘贴前临时切换到 ASCII 输入源 struct InputSourceManager { /// 获取当前输入源 ID static func currentInputSourceID() -> String? { guard let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue() else { return nil } return TISGetInputSourceProperty(source, kTISPropertyInputSourceID) .flatMap { Unmanaged.fromOpaque($0).takeUnretainedValue() as String } } /// 判断当前输入源是否为 CJK 输入法 static func isCJKInputSource() -> Bool { guard let id = currentInputSourceID() else { return false } let cjkPrefixes = [ "com.apple.inputmethod.Kotoeri", // 日语 "com.apple.inputmethod.SCIM", // 简体中文 "com.apple.inputmethod.TCIM", // 繁体中文 "com.apple.inputmethod.Korean", // 韩语 "com.apple.inputmethod.2SetHangul", "com.apple.inputmethod.3SetHangul", "com.apple.inputmethod.390Hangul", "com.apple.inputmethod.GongjinCheong", "com.apple.inputmethod.HNC" ] return cjkPrefixes.contains(where: { id.hasPrefix($0) }) } /// 查找 ASCII 输入源(ABC 键盘) static func findASCIIInputSource() -> TISInputSource? { let properties: [CFString: Any] = [ kTISPropertyInputSourceType: kTISTypeKeyboardLayout as CFString, kTISPropertyInputSourceIsASCIICapable: true as CFBoolean ] guard let list = TISCreateInputSourceList(properties as CFDictionary, false)? .takeRetainedValue() as? [TISInputSource] else { return nil } // 优先选择 "ABC" 或 "U.S." for source in list { if let id = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) .flatMap({ Unmanaged.fromOpaque($0).takeUnretainedValue() as String }) { if id == "com.apple.keylayout.ABC" || id == "com.apple.keylayout.US" { return source } } } // 回退到第一个 ASCII 能力的输入源 return list.first } /// 切换到指定输入源并返回原输入源 static func switchToASCII() -> TISInputSource? { let original = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue() guard let asciiSource = findASCIIInputSource() else { return original } TISSelectInputSource(asciiSource) return original } /// 恢复到指定输入源 static func restore(_ source: TISInputSource?) { guard let source = source else { return } TISSelectInputSource(source) } }