8

给定一个不带修饰符的键的键码,我想产生按 shift+键的结果。示例:对于标准美式键盘,<shift>+<period> 给出 >。

相关功能是 UCKeytranslate,但我需要一些帮助才能正确获取详细信息。下面的代码片段是一个可以在 Xcode 中运行的完整程序。程序的意图被赋予<句点>以产生字符>。

该程序的结果是:

Keyboard: <TSMInputSource 0x10051a930> KB Layout: U.S. (id=0)
Layout: 0x0000000102802000
Status: -50
UnicodeString: 97
String: a
Done
Program ended with exit code: 0

获取布局的部分似乎正在工作,但状态代码显示出了问题。但是什么?

import Foundation
import Cocoa
import Carbon
import AppKit

// The current text input source (read keyboard) has a layout in which
// we can lookup how key-codes are resolved.

// Get the a reference keyboard using the current layout.
var unmanagedKeyboard = TISCopyCurrentKeyboardLayoutInputSource()
var keyboard = unmanagedKeyboard.takeUnretainedValue() as TISInputSource
print("Keyboard: ") ; println(keyboard)

// Get the layout
var ptrLayout   = TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData)
var layout = UnsafeMutablePointer<UCKeyboardLayout>(ptrLayout)
print("Layout: "); println(layout)

// Let's see what the result of pressing  <shift> and <period>  (hopefully the result is > )
var keycode             = UInt16(kVK_ANSI_Period)                           // Keycode for <period>
var keyaction           = UInt16(kUCKeyActionDisplay)                       // The user is requesting information for key display
var modifierKeyState    = UInt32(1 << 17)                                   // Shift
var keyboardType        = UInt32(LMGetKbdType())
var keyTranslateOptions = UInt32(1 << kUCKeyTranslateNoDeadKeysBit)
var deadKeyState        = UnsafeMutablePointer<UInt32>(bitPattern: 0)       // Is 0 the correct value?
var maxStringLength     = UniCharCount(4)                                   // uint32
var actualStringLength  = UnsafeMutablePointer<UniCharCount>.alloc(1)       //
actualStringLength[0]=16
var unicodeString       = UnsafeMutablePointer<UniChar>.alloc(255)
unicodeString[0] = 97 // a (this value is meant to be overwritten by UCKeyTranslate)
var str = NSString(characters: unicodeString, length: 1)
var result = UCKeyTranslate(layout, keycode, keyaction, modifierKeyState, keyboardType, keyTranslateOptions,
                            deadKeyState, maxStringLength, actualStringLength, unicodeString)

// Print the results
print("Status: "); println(result)
var unichar = unicodeString[0];
print("UnicodeString: "); println(String(unichar))
print("String: "); println(str)
println("Done")

编辑

我根据 Ken Thomases 的建议重写了该片段。一些技巧来自:Graphite还使用了使用键码的 Swift 程序。

import Foundation
import Cocoa
import Carbon
import AppKit

// The current text input source (read keyboard) has a layout in which
// we can lookup how key-codes are resolved.

// Get the a reference keyboard using the current layout.
let keyboard = TISCopyCurrentKeyboardInputSource().takeRetainedValue()
let rawLayoutData = TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData)
print("Keyboard: ") ; println(keyboard)

// Get the layout
var layoutData      = unsafeBitCast(rawLayoutData, CFDataRef.self)
var layout: UnsafePointer<UCKeyboardLayout> = unsafeBitCast(CFDataGetBytePtr(layoutData), UnsafePointer<UCKeyboardLayout>.self)
print("Layout: "); println(layout)

print("KbdType "); println(LMGetKbdType()) // Sanity check (prints 44)

var keycode             = UInt16(kVK_ANSI_Period)                         // Keycode for a
var keyaction           = UInt16(kUCKeyActionDisplay)
var modifierKeyState    = UInt32(1 << 1)                                  // Shift
var keyboardType        = UInt32(LMGetKbdType())
var keyTranslateOptions = OptionBits(kUCKeyTranslateNoDeadKeysBit)
var deadKeyState        = UInt32(0)                                       // Is 0 the correct value?
var maxStringLength     = UniCharCount(4)                                 // uint32
var chars: [UniChar]    = [0,0,0,0]
var actualStringLength  = UniCharCount(1)
var result = UCKeyTranslate(layout, keycode, keyaction, modifierKeyState, keyboardType, keyTranslateOptions,
                            &deadKeyState, maxStringLength, &actualStringLength, &chars)
// Print the results
print("Status: "); println(result)
print("Out:"); println(UnicodeScalar(chars[0]))
println("Done")
4

1 回答 1

10

对于kTISPropertyUnicodeKeyLayoutDataTISGetInputSourceProperty()返回一个CFDataRef。您需要获取其字节指针并将其视为指向UCKeyboardLayout. 我不认为这就是你对这条线所做的:

var layout = UnsafeMutablePointer<UCKeyboardLayout>(ptrLayout)

我不太了解 Swift,但它可能会这样工作:

var layout = UnsafePointer<UCKeyboardLayout>(CFDataGetBytePtr(ptrLayout))

或者可能:

var layout = CFDataGetBytePtr(ptrLayout) as UnsafePointer<UCKeyboardLayout>

此外,kUCKeyActionDisplay大多是无用的。它的目的是返回密钥的标签,但它甚至不能可靠地做到这一点。您可能想使用kUCKeyActionDown.

对于修饰符,您希望使用shiftKey右移 8 位的 Carbon 时代位掩码(如 文档中所示UCKeyTranslate())。shiftKey1 << 9,所以shiftKey >> 81 << 1

对于选项,为了简单起见,您应该能够使用kUCKeyTranslateNoDeadKeysMask。它相当于1 << kUCKeyTranslateNoDeadKeysBit.

是的,0 是deadKeyState初始击键或您不想应用任何先前的死键状态的正确值。

我不知道你为什么maxStringLengthuint32. 该类型与最大字符串长度完全无关。是应该写入提供的缓冲区maxStringLength的 UTF-16 代码单元(API 错误地称为“字符”)的最大数量。UCKeyTranslate()它基本上是缓冲区大小,以UniChars(不是字节)为单位。在您的情况下,它应该是 255。或者,由于您可能不希望从一次击键中获得 255 个“字符”,因此您可以减小缓冲区的大小并设置maxStringLength为匹配它的大小。

你的处理方式str很奇怪。在调用之前从unicodeString缓冲区构造它UCKeyTranslate()。您是否期望该字符串对象的值会因为UCKeyTranslate()更改的内容而更改unicodeString?它不是。如果是这样,那将是NSString. 您应该在成功填充该缓冲区NSString从缓冲区构造。当然,你应该在构造时作为长度参数传递。 UCKeyTranslate()actualStringLengthNSString

于 2015-01-01T23:03:38.867 回答