How to use UCKeyTranslate

Given the key code of the pressed key without modifiers, I want to get the result of pressing the shift + key. Example: for a standard American keyboard, <shift> + <period> gives a value.

The relevant function is UCKeytranslate, but I need a little help getting the right information. Below is a snippet of the program, ready to run in Xcode. The purpose of the program is assigned <period> to create the character>.

The result of the program:

Keyboard: <TSMInputSource 0x10051a930> KB Layout: US (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 work, but the status code shows 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 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 rewrote the snippet after Ken Thomases suggestions. A few tricks: Graphite used the Swift program using key codes.

 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") 
+5
source share
1 answer

For kTISPropertyUnicodeKeyLayoutData , TISGetInputSourceProperty() returns a CFDataRef . You need to get your byte pointer and treat it like a pointer to a UCKeyboardLayout . I do not think what you are doing with this line:

 var layout = UnsafeMutablePointer<UCKeyboardLayout>(ptrLayout) 

I really don't know Swift, but it will probably work like:

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

or maybe:

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

In addition, kUCKeyActionDisplay is mostly useless. Its purpose is to return the key label, but it does not even do it reliably. You probably want to use kUCKeyActionDown .

For modifiers, you want to use shiftKey with a carbon era shifted to the right 8 bits (as shown in the documentation for UCKeyTranslate() ). shiftKey is 1 << 9 , so shiftKey >> 8 1 << 1 .

For parameters, you can use kUCKeyTranslateNoDeadKeysMask for simplicity. This is equivalent to 1 << kUCKeyTranslateNoDeadKeysBit .

Yes, 0 is the correct value for deadKeyState for the initial keystroke or for where you do not want to apply the previous state of the dead key.

I'm not sure why you commented on the maxStringLength line using uint32 . This type is not completely associated with the maximum string length. maxStringLength is the maximum number of UTF-16 code blocks (which are incorrectly called API characters), UCKeyTranslate() should write to the provided buffer. This is basically the size of the buffer measured in UniChar (and not bytes). In your case, it should be 255. Or, since you probably do not expect to get 255 “characters” from a single keystroke, you can reduce the buffer size and set maxStringLength as it is.

Your str handling is weird. You create it from the unicodeString buffer before calling UCKeyTranslate() . Do you expect the value of the row object to be changed because UCKeyTranslate() changes the contents of unicodeString ? Is not. If this happened, it would be a very bad mistake in NSString . You must build an NSString from the buffer after UCKeyTranslate() successfully populated this buffer. Of course, you must pass actualStringLength as the length parameter when building the NSString .

+5
source

Source: https://habr.com/ru/post/1210230/


All Articles