import Carbon class KeyboardLayout { /// Return a string ID of the current keyboard input source. static var id: String? { if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(), let sourceIdPointer = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) { let sourceId = Unmanaged.fromOpaque(sourceIdPointer).takeUnretainedValue() return sourceId as String } return nil } /// Translate a physical keyCode to the unmodified character under the current keyboard layout. static func character(forKeyCode keyCode: UInt16) -> String? { guard let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(), let layoutDataPointer = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else { return nil } let layoutData = unsafeBitCast(layoutDataPointer, to: CFData.self) guard let bytes = CFDataGetBytePtr(layoutData) else { return nil } let keyboardLayout = UnsafeRawPointer(bytes).assumingMemoryBound(to: UCKeyboardLayout.self) var deadKeyState: UInt32 = 0 var chars = [UniChar](repeating: 0, count: 4) var length = 0 let status = UCKeyTranslate( keyboardLayout, keyCode, UInt16(kUCKeyActionDisplay), 0, UInt32(LMGetKbdType()), UInt32(kUCKeyTranslateNoDeadKeysBit), &deadKeyState, chars.count, &length, &chars ) guard status == noErr, length > 0 else { return nil } return String(utf16CodeUnits: chars, count: length).lowercased() } }