简体   繁体   English

如何使用 UCKeyTranslate

[英]How to use UCKeyTranslate

Given a key-code for a key pressed without modifiers, I want to produce the result of pressing the shift+key.给定一个没有修饰符的按键的键码,我想产生按下 shift+ 键的结果。 Example: For a standard US keyboard <shift>+<period> gives >.示例:对于标准的美式键盘,<shift>+<period> 给出 >。

The relevant function is UCKeytranslate, but I need a bit of help getting the details right.相关功能是 UCKeytranslate,但我需要一些帮助才能正确了解细节。 The snippet below is a full program ready to run in Xcode.下面的代码片段是一个可以在 Xcode 中运行的完整程序。 The intent of the program is given <period> to produce the character >.程序的意图被赋予 <period> 来产生字符 >。

The result of the program is:程序的结果是:

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

The part that gets the layout seems to be working, but the status code reveals that something went wrong.获取布局的部分似乎可以正常工作,但状态代码显示出了点问题。 But what?但是什么?

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")

EDIT编辑

I have rewritten the snippet following the suggestions of Ken Thomases.我按照 Ken Thomases 的建议重写了这段代码。 A few tricks from: Graphite a Swift program using keycodes was also used.一些技巧来自: 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")

For kTISPropertyUnicodeKeyLayoutData , TISGetInputSourceProperty() returns a CFDataRef .对于kTISPropertyUnicodeKeyLayoutDataTISGetInputSourceProperty()返回一个CFDataRef You need to get its bytes pointer and treat that as the pointer to UCKeyboardLayout .您需要获取其字节指针并将其视为指向UCKeyboardLayout的指针。 I don't think that's what you're doing with this line:我不认为这就是你对这条线所做的:

var layout = UnsafeMutablePointer<UCKeyboardLayout>(ptrLayout)

I don't really know Swift, but it would probably work as:我不太了解 Swift,但它可能会用作:

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

or maybe:或者可能:

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

Also, kUCKeyActionDisplay is mostly useless.此外, kUCKeyActionDisplay大多没用。 Its intent is to return the label of the key, but it doesn't even do that reliably.它的目的是返回密钥的标签,但它甚至不能可靠地做到这一点。 You probably want to use kUCKeyActionDown .您可能想使用kUCKeyActionDown

For the modifiers, you want to use the Carbon-era shiftKey bit mask shifted right 8 bits (as shown in the documentation for UCKeyTranslate() ).对于修饰符,您希望使用碳时代的shiftKey位掩码右移 8 位(如UCKeyTranslate()的文档中所示)。 shiftKey is 1 << 9 , so shiftKey >> 8 is 1 << 1 . shiftKey1 << 9 ,所以shiftKey >> 81 << 1

For the options, you should be able to use kUCKeyTranslateNoDeadKeysMask for simplicity.对于选项,为了简单起见,您应该能够使用kUCKeyTranslateNoDeadKeysMask It's equivalent to 1 << kUCKeyTranslateNoDeadKeysBit .它相当于1 << kUCKeyTranslateNoDeadKeysBit

Yes, 0 is the proper value for deadKeyState for an initial keystroke or one where you don't want to apply any previous dead-key state.是的,0 是用于初始击键或您不想应用任何先前死键状态的deadKeyState的正确值。

I'm not sure why you've commented the maxStringLength line with uint32 .我不确定你为什么用uint32评论maxStringLength行。 That type is completely unrelated to the maximum string length.该类型与最大字符串长度完全无关。 maxStringLength is the maximum number of UTF-16 code units (what the APIs incorrectly call "characters") the UCKeyTranslate() should write to the provided buffer. maxStringLengthUCKeyTranslate()应写入提供的缓冲区的最大 UTF-16 代码单元数(API 错误地称之为“字符”)。 It's basically the buffer size, measured in UniChar s (not bytes).它基本上是缓冲区大小,以UniChar s(而不是字节)为单位。 In your case it should be 255. Or, since you probably don't expect to get 255 "characters" from a single keystroke, you could reduce the size of the buffer and set maxStringLength to match whatever it is.在您的情况下,它应该是 255。或者,由于您可能不希望从一次击键中获得 255 个“字符”,您可以减少缓冲区的大小并设置maxStringLength以匹配它是什么。

Your handling of str is strange.你对str处理很奇怪。 You construct it from the unicodeString buffer before calling UCKeyTranslate() .您在调用UCKeyTranslate()之前从unicodeString缓冲区构造它。 Do you expect that string object's value to be changed because UCKeyTranslate() changes the contents of unicodeString ?您是否希望因为UCKeyTranslate()更改unicodeString的内容而更改字符串对象的值? It does not.它不是。 If it did, that would be a very bad bug in NSString .如果确实如此,那将是NSString一个非常糟糕的错误。 You should construct the NSString from the buffer after UCKeyTranslate() has successfully populated that buffer.UCKeyTranslate()成功填充该缓冲区后,您应该从缓冲区构造NSString Of course, you should pass actualStringLength as the length parameter when constructing the NSString .当然,在构造NSString时,您应该将actualStringLength作为长度参数传递。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM