Jef*_*f J 5 macos-carbon keyboard-events swift
我知道其他人也问了类似的问题,但我还没有看到明确的答案,我仍然被困住了.我正在尝试编写一个Swift函数,它接受硬件生成的键盘扫描代码,例如来自NSEvent,并返回密钥的alpha-caps-locked名称,用于特定的键排列(Dvorak,Qwerty等). )当前在OS中有效(可能与生成代码时生效的安排不同).
我的理解是,这样做的唯一方法就是调用一些非常古老的Carbon功能,避开Swift的极端类型安全性,这是我感觉不舒服的事情.这是The Show So Far:
import Cocoa
import Carbon
func keyName (scanCode: UInt16) -> String?
{ let maxNameLength = 4, modifierKeys: UInt32 = 0x00000004 // Caps Lock (Carbon Era)
let deadKeys = UnsafeMutablePointer<UInt32>(bitPattern: 0x00000000),
nameBuffer = UnsafeMutablePointer<UniChar>.alloc(maxNameLength),
nameLength = UnsafeMutablePointer<Int>.alloc(1),
keyboardType = UInt32(LMGetKbdType())
let source = TISGetInputSourceProperty ( TISCopyCurrentKeyboardLayoutInputSource()
.takeRetainedValue(),
kTISPropertyUnicodeKeyLayoutData )
let dataRef = unsafeBitCast(source, CFDataRef.self)
let dataBuffer = CFDataGetBytePtr(dataRef)
let keyboardLayout = unsafeBitCast(dataBuffer, UnsafePointer <UCKeyboardLayout>.self)
let osStatus = UCKeyTranslate (keyboardLayout, scanCode, UInt16(kUCKeyActionDown),
modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask),
deadKeys, maxNameLength, nameLength, nameBuffer)
switch osStatus
{ case 0: return NSString (characters: nameBuffer, length: nameLength[0]) as String
default: NSLog (“Code: 0x%04X Status: %+i", scanCode, osStatus); return nil }
}
Run Code Online (Sandbox Code Playgroud)
它不会崩溃,在这一点上我几乎认为游戏成就本身,但它也不起作用.UCKeyTranslate总是返回-50的状态,我理解这意味着有一个参数错误.我怀疑"keyboardLayout",因为它是最复杂的设置.谁能看到参数问题?或者是否有更新的框架来处理这类事情?
正如你已经发现了,你必须通过地址一的UInt32
变量作为deadKeyState参数.分配内存是解决该问题的一种方法,但是你最好不要忘记释放内存,否则程序会泄漏内存.
另一种可能的解决方案是将变量的地址作为inout-argument 传递给&:
var deadKeys : UInt32 = 0
// ...
let osStatus = UCKeyTranslate(..., &deadKeys, ...)
Run Code Online (Sandbox Code Playgroud)
这样更短更简单,您无需释放内存.同样适用于nameBuffer和nameLength.
该unsafeBitCast()可以通过使用来避免Unmanaged类型,比较斯威夫特:CFArray:获得价值为UTF字符串了类似的问题,更详细的解释.
您还可以利用的免费桥接的优势
CFData和NSData.
然后你的函数看起来像这样(Swift 2):
import Carbon
func keyName(scanCode: UInt16) -> String?
{
let maxNameLength = 4
var nameBuffer = [UniChar](count : maxNameLength, repeatedValue: 0)
var nameLength = 0
let modifierKeys = UInt32(alphaLock >> 8) & 0xFF // Caps Lock
var deadKeys : UInt32 = 0
let keyboardType = UInt32(LMGetKbdType())
let source = TISCopyCurrentKeyboardLayoutInputSource().takeRetainedValue()
let ptr = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData)
let layoutData = Unmanaged<CFData>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue() as NSData
let keyboardLayout = UnsafePointer<UCKeyboardLayout>(layoutData.bytes)
let osStatus = UCKeyTranslate(keyboardLayout, scanCode, UInt16(kUCKeyActionDown),
modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask),
&deadKeys, maxNameLength, &nameLength, &nameBuffer)
guard osStatus == noErr else {
NSLog("Code: 0x%04X Status: %+i", scanCode, osStatus);
return nil
}
return String(utf16CodeUnits: nameBuffer, count: nameLength)
}
Run Code Online (Sandbox Code Playgroud)
Swift 3更新:
import Carbon
func keyName(scanCode: UInt16) -> String? {
let maxNameLength = 4
var nameBuffer = [UniChar](repeating: 0, count : maxNameLength)
var nameLength = 0
let modifierKeys = UInt32(alphaLock >> 8) & 0xFF // Caps Lock
var deadKeys: UInt32 = 0
let keyboardType = UInt32(LMGetKbdType())
let source = TISCopyCurrentKeyboardLayoutInputSource().takeRetainedValue()
guard let ptr = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else {
NSLog("Could not get keyboard layout data")
return nil
}
let layoutData = Unmanaged<CFData>.fromOpaque(ptr).takeUnretainedValue() as Data
let osStatus = layoutData.withUnsafeBytes {
UCKeyTranslate($0, scanCode, UInt16(kUCKeyActionDown),
modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask),
&deadKeys, maxNameLength, &nameLength, &nameBuffer)
}
guard osStatus == noErr else {
NSLog("Code: 0x%04X Status: %+i", scanCode, osStatus);
return nil
}
return String(utf16CodeUnits: nameBuffer, count: nameLength)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
782 次 |
| 最近记录: |