在Swift中获取键盘代码的键名 [英] Getting key names for keyboard codes in Swift
问题描述
我知道其他人也曾问过类似的问题,但是我没有找到确切的答案,但我仍然很困惑.我正在尝试编写一个Swift函数,该函数接受硬件生成的键盘扫描代码(例如来自NSEvent的代码),并针对特定的键排列(Dvorak,Qwerty等)返回键的Alpha大小写锁定名称. )目前在操作系统中有效(可能与生成代码时有效的安排不同).
I know others have asked similar questions, but I haven’t seen a definitive answer, and I’m still stuck. I’m trying to write a Swift function that takes a hardware-generated keyboard scan code, such as from an NSEvent, and returns the alpha-caps-locked name of the key, for the particular key arrangement (Dvorak, Qwerty, etc.) currently in effect in the OS (which might be different from the arrangement in effect when the code was generated).
据我了解,唯一的方法就是调用一些非常古老的Carbon函数,从而避开了许多Swift的极端类型安全性,这让我感到不舒服.这是到目前为止的演出:
It’s my understanding that the only way to do this is to invoke some very old Carbon functions, skirting a lot of the Swift’s extreme type-safety, something I don’t feel comfortable doing. Here is 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 }
}
它不会崩溃,在这一点上,我几乎认为它本身就是一项游戏成就,但它都不起作用. UCKeyTranslate始终返回状态-50,据我了解,这意味着参数错误.我怀疑"keyboardLayout"是最复杂的设置.谁能看到参数问题?还是针对此类问题有更新的框架?
It doesn’t crash, which at this point I almost consider a game achievement in itself, but neither does it work. UCKeyTranslate always returns a status of -50, which I understand means there’s a parameter wrong. I suspect "keyboardLayout," as it is the most complicated to set up. Can anyone see the parameter problem? Or is there a more up-to-date framework for this sort of thing?
推荐答案
您已经发现,必须传递UInt32
的地址
变量作为deadKeyState
参数.分配内存是一个
解决问题的方法,但您一定不要忘记释放内存
最终,否则程序将泄漏内存.
As you already found out, you have to pass the address of a UInt32
variable as the deadKeyState
argument. Allocating memory is one
way to solve that problem, but you must not forget to free the memory
eventually, otherwise the program will leak memory.
另一种可能的解决方案是将变量的地址传递为
&
的inout参数:
Another possible solution is to pass the address of a variable as
an inout-argument with &
:
var deadKeys : UInt32 = 0
// ...
let osStatus = UCKeyTranslate(..., &deadKeys, ...)
这有点短和简单,您不需要释放
记忆.相同的内容可以应用于nameBuffer
和nameLength
.
This is a bit shorter and simpler, and you don't need to release the
memory. The same can be applied to nameBuffer
and nameLength
.
通过使用Unmanaged
类型可以避免使用unsafeBitCast()
,
比较 Swift:CFArray:将值作为UTF字符串获取对于类似的问题,
更详细的解释.
The unsafeBitCast()
can be avoided by using the Unmanaged
type,
compare Swift: CFArray : get values as UTF Strings for a similar problem and
more detailed explanations.
您还可以利用免费电话之间的免费桥接
CFData
和NSData
.
Also you can take advantage of the toll-free bridging between
CFData
and NSData
.
然后您的功能应如下所示( Swift 2 ):
Then your function could look like this (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)
}
针对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)
}
针对Swift 4的更新:
从Swift 4开始,Data.withUnsafeBytes
用UnsafeRawBufferPointer
调用闭包,该UnsafeRawBufferPointer
必须绑定 指向UCKeyboardLayout
的指针:
As of Swift 4, Data.withUnsafeBytes
calls the closure with a UnsafeRawBufferPointer
which has to be bound a pointer to UCKeyboardLayout
:
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.bindMemory(to: UCKeyboardLayout.self).baseAddress, 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)
}
这篇关于在Swift中获取键盘代码的键名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!