Как использовать UCKeyTranslate

Учитывая код клавиши для клавиши, нажатой без модификаторов, я хочу получить результат нажатия клавиши 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")

РЕДАКТИРОВАТЬ

Я переписал фрагмент, следуя советам Кена Томаса. Несколько уловок от: 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")

person soegaard    schedule 01.01.2015    source источник
comment
Я получаю сообщение об ошибке Use of undeclared type 'UCKeyboardLayout' с этим кодом. Есть идеи, как это исправить?   -  person Abhi Beckert    schedule 12.06.2015
comment
В документации к 10.10.3 говорится, что структура UCKeyboardLayout была удалена. Вам нужно посмотреть, что пришло на смену. developer.apple.com/library/mac/releasenotes/ Общие /   -  person soegaard    schedule 12.06.2015
comment
PS: Я попытался найти его, но в документации для UCKeyboardLayout сейчас вообще не упоминается Swift: developer.apple.com/library/mac/documentation/Carbon/Reference/   -  person soegaard    schedule 12.06.2015
comment
PPS: у других такая же проблема: stackoverflow.com/questions/29531871/   -  person soegaard    schedule 12.06.2015


Ответы (1)


Для kTISPropertyUnicodeKeyLayoutData TISGetInputSourceProperty() возвращает 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 бит (как показано в документации для UCKeyTranslate()). shiftKey равно 1 << 9, поэтому shiftKey >> 8 равно 1 << 1.

Для опций вы можете использовать kUCKeyTranslateNoDeadKeysMask для простоты. Это эквивалент 1 << kUCKeyTranslateNoDeadKeysBit.

Да, 0 - правильное значение для deadKeyState для начального нажатия клавиши или того, при котором вы не хотите применять какое-либо предыдущее состояние мертвой клавиши.

Я не уверен, почему вы прокомментировали строку maxStringLength с помощью uint32. Этот тип совершенно не связан с максимальной длиной строки. maxStringLength - максимальное количество единиц кода UTF-16 (то, что API неправильно называют «символами»), UCKeyTranslate() должен записать в предоставленный буфер. В основном это размер буфера, измеряемый в UniChars (не в байтах). В вашем случае это должно быть 255. Или, поскольку вы, вероятно, не ожидаете получить 255 «символов» от одного нажатия клавиши, вы можете уменьшить размер буфера и установить maxStringLength в соответствии с тем, что он есть.

Вы странно обращаетесь с str. Вы создаете его из unicodeString буфера перед вызовом UCKeyTranslate(). Ожидаете ли вы, что значение этого строкового объекта изменится, потому что UCKeyTranslate() изменяет содержимое unicodeString? Это не. Если бы это было так, это было бы очень серьезной ошибкой в ​​NSString. Вы должны создать NSString из буфера после того, как UCKeyTranslate() успешно заполнит этот буфер. Конечно, вы должны передать actualStringLength в качестве параметра длины при создании NSString.

person Ken Thomases    schedule 01.01.2015
comment
Большое спасибо за ваши предложения. Я чувствую, что приближаюсь к решению - мне просто нужно взломать проблему CFDataGetBytePtr. Причина комментария uint32 в том, что я пишу привязки для UCKeyTranslate и мне нужен базовый тип C. Программа Swift предназначена для получения рабочего примера, который я могу воспроизвести с помощью FFI. - person soegaard; 02.01.2015
comment
Спасибо за обстоятельный ответ. - person soegaard; 02.01.2015